BlackWaspTM

This web site uses cookies. By using the site you accept the cookie policy.This message is for compliance with the UK ICO law.

Windows Presentation Foundation
.NET 4.0+

WPF Base Classes - DependencyObject

The nineteenth part of the Windows Presentation Foundation Fundamentals tutorial continues the description of the base classes for the WPF layout controls, which are shared by other controls too. This article describes the DependencyObject class.

Dependency Properties

Early in the WPF tutorial, in the article describing the Canvas layout control, I mentioned the concept of dependency properties. This special type of property is key to the operation of WPF controls. Dependency properties provide additional functionality over standard common language runtime (CLR) properties.

Usually a CLR property has a value that can be changed using a set accessor and retrieved with a get accessor. Dependency properties are more complex. Although you can set a local value for a control's property, this is not always the effective value. The effective value, which determines the rendering or behaviour of the control, takes values from various sources. This includes the local value, defaults, information from data binding, inherited values from parent control properties or attached properties, values from styles that are applied to a control and intermediate values from animations.

As an example, you might set a StackPanel's background colour to yellow. If the control is configured so that the colour changes to red when the user moves the mouse pointer within its boundaries, the yellow value will be overridden as the cursor passes over the control. If you've included an animation of the background colour in response to the mouse over event, the StackPanel may appear in various shades of orange during the change.

The functionality required for the operation of dependency properties is provided by the DependencyObject class. This is a base class for all of the layout controls that we've seen in the tutorial and for other controls that we'll examine in future articles. DependencyObject includes several methods that are useful for retrieving, changing and clearing the values of a control's dependency properties. These are the focus of the remainder of this article.

Obtaining a Dependency Property's Effective Value

In most cases, when you wish to obtain the value from a control's dependency property you will want the effective value. This is the value calculated by the framework, which considers information from all possible sources. For visual properties, it's the value that determines what the user can see. This is often different to the local value, which is manually applied to the control through XAML, code or data binding.

You can retrieve the effective value of a dependency property using the GetValue method. The method returns an object instance that must be cast to the correct underlying type to be useful. The method requires a single parameter, which describes the dependency property to be read. All dependency properties have corresponding items defined as static, read-only fields of their containing type. By convention, the fields are named using the name of the property and the suffix, "Property".

type value = (type)control.GetValue(dependency-property);

To demonstrate the GetValue method, and the other methods that we shall see shortly, create a new WPF Application project in Visual Studio, naming the project, "DependencyObjectDemo". Replace the XAML of the automatically generated window with the code shown below. This creates a Grid with one row and two columns. The rightmost cell hosts a button.

<Window x:Class="DependencyObjectDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="DependencyObject Demo"
        Width="250"
        Height="200">
    <Grid Background="Cornsilk">
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Button Grid.Column="1" Name="MyButton" Content="Click Me" Click="ButtonClick"/>
    </Grid>
</Window>

The button includes a Click event where we'll add code to read a dependency property and display its effective value. In this case we'll read the attached property, "Column", which is defined by the Grid class but applied to the button. This means we'll call the button's GetValue method, passing the Grid's ColumnProperty field.

Switch to the code view for the window and add the following method.

private void ButtonClick(object sender, RoutedEventArgs e)
{
    int value = (int)MyButton.GetValue(Grid.ColumnProperty);
    MyButton.Content = "Column " + value.ToString();
}

Try running the program and clicking the button. You should see that the column number is correctly read and included in the button's updated caption.

Obtaining a Dependency Property's Local Value

When you need the local value of a property, rather than its effective value, you can obtain it using ReadLocalValue. The returned value is that which has been specified using code, XAML or data binding. Any changes to the effective value cause by defaults, inherited properties, animation or styles are ignored.

The syntax for ReadLocalValue is identical to that of GetValue, except for the method name. Let's try it. Change the code within the ButtonClick method to that shown below.

int value = (int)MyButton.ReadLocalValue(Grid.ColumnProperty);
MyButton.Content = "Column " + value.ToString();

Try running the program and clicking the button. The results will be identical to the previous example because the local and effective values match in this case; the local value is set to one in the XAML. However, if we remove the locally applied value we'll see a different result.

Change the XAML for the button as follows, removing the attribute for Grid.Column:

<Button Name="MyButton" Content="Click Me" Click="ButtonClick"/>

Try running the program again. This time clicking the button throws an exception. This happens because, when there is no local value, ReadLocalValue returns UnsetValue, which is a field defined in the DependencyProperty class. UnsetValue cannot be converted to an integer so it is the cast operator that causes the failure.

We can test for a missing local value by modifying the code. Try changing the method's content as follows:

if (MyButton.ReadLocalValue(Grid.ColumnProperty) == DependencyProperty.UnsetValue)
{
    MyButton.Content = "Column Unset";
}
else
{
    int value = (int)MyButton.ReadLocalValue(Grid.ColumnProperty);
    MyButton.Content = "Column " + value.ToString();
}

Clicking the button now changes the button's text to "Column Unset".

NB: GetValue will never return Unset. If no local value is present and the other data sources don't set a value for a property, GetValue will return the default value. In the case of our button's Grid.Column property, the default value is zero.

26 June 2013