Monthly Archives: April 2010

Custom Attached Properties in Silverlight

An attached property is a type of global property that is settable on any nested object. In Silverlight (and WPF), attached properties are usually defined as a specialized case of Dependency Properties, and like all dependency properties they are managed by the Silverlight property system. Attached properties differ in that they do not have the conventional "property wrapper".

Most commonly, attached properties are used in layout containers. Every control in XAML has its own set of internal properties. However, when this control is placed inside a container, it acquires additional features depending on the type of the container. For example, the Grid.Row property is created as an attached property because any control inside a Grid needs to specify the grid cell where it is positioned. For example:

<Grid x:Name="OuterGrid">
	<TextBox x:Name="innerBox" Grid.Row="1"/>
</Grid>

Since the TextBox is nested inside the Grid, it gains the Row property which "attaches" to the TextBox instance. In essence, attached properties allow different child elements to specify values for a property defined in a parent element. Attached properties always use a two-part name of the form: AttachedPropertyProvider.PropertyName (OR simply DefiningType.PropertyName)

 

Defining a Custom Attached Property:

Creating an attached property is similar to how we create a custom dependency property with a few subtle differences. Since I seem to love widgets so much, I will continue to use a custom WidgetControl as an example. In one of my previous posts we saw how to create a custom dependency property for WidgetControl. In this post, we will see how to create the WidgetStateProperty as an attached property.

1. The first step is to define a static readonly DependencyProperty object that represents the property, and register this attached property with Silverlight. This is done via the Dependency.RegisterAttached() method.

        public static readonly DependencyProperty WidgetStateProperty =
            DependencyProperty.RegisterAttached(
                "WidgetState",
                typeof(Boolean),
                typeof(WidgetControl),
                new PropertyMetadata(false));

Notice that when we were creating custom dependency properties, we used the DependencyProperty.Register() method. The parameters for both these methods are exactly the same: the property name (WidgetState in this example), the data type used by the property (bool), the type that owns this property (WidgetControl), and a PropertyMetadata object that provides additional information, such as default values.

2. Our attached property provider must also provide static GetXXX() and SetXXX() methods, where XXX is the name of our property (WidgetState).

        public static void SetWidgetState(UIElement element, Boolean value)
        {
            element.SetValue(WidgetControl.WidgetStateProperty, value);
        }

        public static Boolean GetWidgetState(UIElement element)
        {
            return (Boolean)element.GetValue(WidgetControl.WidgetStateProperty);
        }

We now have a fully functioning attached property which can be set using the DefiningType.PropertyName syntax. The complete code for this WidgetControl is shown below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace Attached
{
    public partial class WidgetControl : UserControl
    {
        public WidgetControl()
        {
            InitializeComponent();
        }

        public static readonly DependencyProperty WidgetStateProperty =
            DependencyProperty.RegisterAttached(
                "WidgetState",
                typeof(Boolean),
                typeof(WidgetControl),
                new PropertyMetadata(false));


        public static void SetWidgetState(UIElement element, Boolean value)
        {
            element.SetValue(WidgetControl.WidgetStateProperty, value);
        }

        public static Boolean GetWidgetState(UIElement element)
        {
            return (Boolean)element.GetValue(WidgetControl.WidgetStateProperty);
        }
    }
}

Here's a very simple example where we use WidgetControl and its WidgetState property. The code is in XAML:

<UserControl x:Class="Attached.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:Attached"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

    <Grid x:Name="LayoutRoot" Background="White">
        <local:WidgetControl>
            <TextBox x:Name="txtBox" local:WidgetControl.WidgetState="true">
            </TextBox>
        </local:WidgetControl>
    </Grid>
</UserControl>

Finally, a couple of good resources that talk more about dependecy and attached properties are here [msdn.microsoft.com] and here [www.silverlightshow.net]

Leave a comment

Filed under .NET, Programming

Resolution of Dependency Properties

As I mentioned in my previous blog post, dependency properties depend on any number of services (called property providers) for their values. The Silverlight runtime needs to follow strict rules of precedence to determine the value of a dependency property. This process is called dynamic value resolution.

Here’s a prioritized list of providers in their order of precedence:

1. Animation: If an animation is running and is changing the property value, Silverlight uses the animated value

2. Local Value: If the property value has been explicitly set through XAML or code (by using SetValue or the property wrapper), Silverlight uses the local value.

3. Templated Properties: Silverlight uses the template properties if the object has been built dynamically from templates (ControlTemplate or DataTemplate).

4. Styles Setters: If the page or the application resource defines a Style that applies to the given element, with a Setter for its dependency property, then Silverlight assigns the value from the Setter to the dependency property.

5. Default Value: If no other property setter is at work, Silverlight assigns the default value to the dependency property. If a value is passed in through the PropertyMetadata object during dependency property registration, it will be used as the default value. If no value has been passed in, then the default value depends on the type (reference or value) used for storing that dependency property.

1 Comment

Filed under .NET, Programming

Dependency Properties in Silverlight

Very simply, a property of an element that depends on a number of property-providers outside the element is called a "dependency property". Each of these providers has its own level of precedence. These properties can be either set directly by code, or by one of Silverlight’s services (i.e. property providers) such as animation, data binding, styles etc.

Dependency properties were originally introduced in WPF and are a key concept in both Silverlight and WPF. These dependency features are designed to be read and set in code just like normal properties. However, the internal implementation of dependency properties differs from the implementation of ordinary properties.

To better understand dependency properties, let’s consider an example (this example has been taken and modified from this excellent blog post ):

<UserControl x:Class="DependencyProperty.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

    <Grid x:Name="LayoutRoot" Background="White">
        <ItemsControl Foreground="Red">
            <TextBlock Foreground="Blue">
                1st line of text
            </TextBlock>
            <TextBlock>
                2nd line of text
            </TextBlock>
        </ItemsControl>

    </Grid>
</UserControl>

In this example, we use two TextBlock controls to display two lines of text. The results of running this app are shown below:

Results of running the Silverlight app above

The first TextBlock changes its Foreground property to Blue. Once we run this Silverlight app, we see that the first line of text has blue color, which matches our intuition since we did set the Foreground property to Blue. However, the second line of text is red. This is somewhat surprising (especially for folks who are new to WPF/Silverlight). I never set the Foreground property of the second TextBlock , so why is it red?

This is an example of dependency properties. Foreground of a TextBlock is a dependency property and the Silverlight runtime sets it to red, which is the value of the Foreground property of the encapsulating ItemsControl .

Defining a Dependency Property

The syntax for creating a dependency property is different than creating an ordinary .NET property. Let's look at how to add a dependency property to a custom WidgetControl.

1. The first step is to define the object that represents the property. This is an instance of DependencyProperty class. This object must be defined as static readonly field. By convention, this field has the name of the ordinary property plus the post-fix Property. For example, if my new property is called WidgetState, the DependencyProperty object will be called WidgetStateProperty.

         public static readonly DependencyProperty WidgetStateProperty;

2. The second step is to register the dependency property with Silverlight. This step needs to be completed before any code uses the property, so it must be performed in a static constructor.

        static WidgetControl()
        {
            WidgetStateProperty = DependencyProperty.Register(
                "WidgetState", 
                typeof(Boolean), 
                typeof(WidgetControl),
                new PropertyMetadata(false));
        }

The arguments to DependencyProperty.Register() method are: the property name (WidgetState in this example), the data type used by the property (Boolean), the type that owns this property (WidgetControl), and a PropertyMetadata object that provides additional information. In this example, I am simply passing a default value of "false" to the PropertyMetadata constructor

3. Define a CLR wrapper property whose names matches the name of the dependency property.

        public Boolean WidgetState
        {
            get
            {
                return (Boolean)GetValue(WidgetStateProperty);
            }
            set
            {
                SetValue(WidgetStateProperty, value);
            }
        }

We now have a fully functioning dependency property which can be set just like any other .NET property using the property wrapper

	widgetCtrl.WidgetState = True;

OR, using XAML:

        <cnmspc:WidgetControl x:Name="widgetCtrl" WidgetState="True">
        </cnmspc:WidgetControl>

The complete code for this WidgetControl is shown below:

    public partial class WidgetControl : UserControl
    {
        public WidgetControl()
        {
            InitializeComponent();
        }

        public static readonly DependencyProperty WidgetStateProperty;

        static WidgetControl()
        {
            WidgetStateProperty = DependencyProperty.Register(
                "WidgetState", 
                typeof(Boolean), 
                typeof(WidgetControl),
                new PropertyMetadata(false));
        }

        public Boolean WidgetState
        {
            get
            {
                return (Boolean)GetValue(WidgetStateProperty);
            }
            set
            {
                SetValue(WidgetStateProperty, value);
            }
        }
    }

5 Comments

Filed under .NET, Programming