Working with dependency properties

If I had to sum up the dependency properties in a single term, I would say that dependency properties are the regular property systems on steroids. The dependency properties really take what you know about properties to a whole new level, but that is also why it is one of the hardest concepts in Silverlight to grasp.

Most properties used on the UI objects in Silverlight use dependency properties. The most common place to write your own dependency properties is when writing your own control. The dependency properties are necessary when you wish to use data binding or styling, so if you think back about the projects we created in this book so far, you've used quite a lot of dependency properties but didn't even know that. Every time you bind a property in XAML, you're actually using a dependency property.

To make my point clearer, let's assume that you are writing a custom control. Other than the regular properties you get from inheriting from control, you wish to let users change the background of the control either by binding to the exposed property or setting its value directly from XAML. The only way this background property is going to be able to achieve these requirements is if it is a dependency property.

The dependency properties obtain their value from different sources. Their real purpose is to compute the value of a property based on values of other inputs such as animations, templates, and so on.

The dependency properties are best explained through example, so without further ado, let's get introduced to our first dependency property!

The structure of dependency properties

As stated in the preceding section, creating a dependency property is a bit more difficult than writing a regular property. The process of writing a dependency property includes registering it with the dependency property system, writing a public wrapper for it, and optionally, writing some actions in the PropertyChangedCallback event handler.

A typical property in C# looks as follows:

public string CakeColor
{
get;
set;
}

A dependency property of the same CakeColor will looks as follows:

public static readonly DependencyProperty CakeColorProperty = DependencyProperty.Register("CakeColor", typeof(string), typeof(CakeProperties), new PropertyMetadata(""));

But wait, that's not all. This is just the registering code for the property; to actually use it, we need to write a public CLR wrapper for it as well:

public string CakeColor
{
get { return (string)GetValue(CakeColorProperty); }
set { SetValue(CakeColorProperty, value); }
}

The wrapper provides an easy way to get and set the value of CakeColorProperty, using a getter and a setter, just like any regular property. The major difference here is that instead of returning or setting values directly, you have to call the GetValue and SetValue methods with the name of the dependency property as their value.

While the wrapper represents how to use the dependency property, to register the property itself, we use the dependency property identifier, which is what CakeColorProperty is.

The first part of the identifier is its declaration:

public static readonly DependencyProperty CakeColorProperty

You declare the CakeColorProperty property of the DependencyProperty type, which is set to readonly and in the majority of cases, public and static.

The second part of the identifier is its initialization:

DependencyProperty.Register("CakeColor", typeof(string), typeof(CakeProperties), new PropertyMetadata(""));

The initialization process is used to register our property in a collection that is not visible and only the Silverlight runtime has access to it. The runtime uses this collection to keep track of all the dependency properties used in the application and manage them.

To register a dependency property, we have to use the DependencyProperty.Register method. This method requires a few arguments that help us customize some of the dependency property behaviors.

The first argument we need to supply is the name of the dependency property we wish to register. In our previous example, the name is CakeColor. To make things easier down the road, always use the same name you used in the declaration part of the property minus the property part (CakeColorProperty - CakeColor).

The second argument we supply to the Register method is the type of our property. In our case, the cake color will be represented by text, so the type of the property will be string.

Next, we need to specify the type of class that the dependency property is part of. This property will vary a lot between projects as you will usually host your dependency properties in different classes. In our example, the dependency property resides in a class called CakeProperties and, as such, this is what we supply to the Register method.

The last argument defines the metadata that will be used throughout the use of our dependency property. The metadata is managed by the PropertyMetadata class and can be used for setting the default value of the dependency property, setting a method to call when the property's value changes, or setting both of them.

Let's create a new dependency property to help sink in all the information we talked about.

Creating your first dependency property

Open the Chapter4-DP project from the downloadable book files from the Packt website in Visual Studio 2010.

The project contains a user control called CircleText, which resides in the CircleText.xaml file. Let's add a few instances of the CircleText control to the page. Open the MainPage.xaml file, and add the following code snippet under the LayoutRoot Grid control:

<local:CircleText x:Name="circleText1" Width="120" Height="120" Margin="12,12,268,168" />
<local:CircleText HorizontalAlignment="Left" Width="80" Height="80" x:Name="circleText2" VerticalAlignment="Top" Margin="290,122,0,0" RenderTransformOrigin="0.5,0.5" >
<local:CircleText.RenderTransform>
<CompositeTransform Rotation="36.135"/>
</local:CircleText.RenderTransform>
</local:CircleText>
<local:CircleText HorizontalAlignment="Left" Width="100" Height="100" Margin="83,184,0,0" x:Name="circleText3" VerticalAlignment="Top" RenderTransformOrigin="0.5,0.5" UseLayoutRounding="False" d:LayoutRounding="Auto" d:LayoutOverrides="HorizontalAlignment" >
<local:CircleText.RenderTransform>
<CompositeTransform Rotation="-40.683"/>
</local:CircleText.RenderTransform>
</local:CircleText>

Build and run the application. Now, you should see something similar to the following screenshot:

Creating your first dependency property

As you can see from the preceding screenshot, all the user controls have the same text hardcoded in them. As this is hardly what we want from a user control, we are going to add a dependency property to handle the text inside the circle.

The first thing we have to do is to write the public wrapper of the dependency property. As we mentioned earlier, this will be the gate for communicating with the property.

Open the CircleText.xaml.cs file, and add the following code snippet below the control's constructor:

public string SetTextBox
{
get
{
return (string)GetValue(SetTextBoxProperty);
}
set
{
SetValue(SetTextBoxProperty, value);
}
}

Now that we have the wrapper, it's time to register SetTextBoxProperty itself. Add the following code snippet right below the preceding code snippet:

public static readonly DependencyProperty SetTextBoxProperty = DependencyProperty.Register("SetTextBox", typeof(string), typeof(CircleText), new PropertyMetadata(new PropertyChangedCallback(SetNewText)));

We are registering our new property—SetTextBox, with the type of string. The parent type is CircleText because this property resides inside the CircleText user control. As we want the text inside the circle to change every time someone sets the property, we need to set a callback method to handle the change. In our case, we are telling the property to call the SetNewText method once someone sets the SetTextBox property. Let's define the SetNewText method now. Add the following code snippet below the dependency property registration code:

private static void SetNewText(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
(d as CircleText).tbCircle.Text = e.NewValue.ToString();
}

There are a few things to notice in this callback method as follows:

  • The method must be defined as static because the registration code of the dependency property itself is static.
  • The method must receive two arguments—DependencyObject and DependencyPropertyChangedEventArgs. The first argument represents the object of the property (CircleText in our case) and the second one refers to the eventargs of the callback.

Once the SetNewText method gets called, it casts DependencyObject to the user control class that runs it and sets its inner tbCircle (the TextBox control) text property to the value that the dependency property was set to.

Build the application. If you run it right now, you won't notice anything different as we haven't set the new dependency property value to anything yet.

Switch back to the MainPage.xaml file, and on any of the CircleText elements you added, set the new SetTextBox property to a value of your choice. An example of such value can be as follows:

<local:CircleText x:Name="circleText1" SetTextBox="I'm a red circle!" Width="120" Height="120" Margin="12,12,268,168" />

Build and run your application. You should expect the result, as shown in the following screenshot:

Creating your first dependency property

That concludes our exercise on adding the dependency properties. For some extra bragging points, try to create a dependency property for the circle's background color. If you follow the preceding steps, you will notice that only small changes to the code are required!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.58.51.36