Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Windows Presentation Foundation 4.5 Cookbook

You're reading from   Windows Presentation Foundation 4.5 Cookbook For C# developers, this book offers a fast route to getting more closely acquainted with the ins and outs of Windows Presentation Foundation. The recipe approach smoothes out the complexities and enhances learning.

Arrow left icon
Product type Paperback
Published in Sep 2012
Publisher Packt
ISBN-13 9781849686228
Length 464 pages
Edition 1st Edition
Languages
Arrow right icon
Author (1):
Arrow left icon
Pavel Yosifovich Pavel Yosifovich
Author Profile Icon Pavel Yosifovich
Pavel Yosifovich
Arrow right icon
View More author details
Toc

Table of Contents (18) Chapters Close

Windows Presentation Foundation 4.5 Cookbook
Credits
About the Author
About the Reviewers
www.PacktPub.com
Preface
1. Foundations 2. Resources FREE CHAPTER 3. Layout and Panels 4. Using Standard Controls 5. Application and Windows 6. Data Binding 7. Commands and MVVM 8. Styles, Triggers, and Control Templates 9. Graphics and Animation 10. Custom Elements 11. Threading Index

Creating a dependency property


Dependency properties are the workhorse of WPF. This infrastructure provides for many of WPF's features, such as data binding, animations, and visual inheritance. In fact, most of the various element properties are Dependency Properties. Sometimes we need to create such properties for our own controls or windows.

Getting ready

Make sure you have Visual Studio up and running.

How to do it...

We'll create a simple user control with one new dependency property to illustrate the entire procedure:

  1. Within Visual Studio 2010, create a new WPF Application named CH01.DependencyProperties.

  2. We'll add a simple User Control, to which we'll add a dependency property. Don't worry if you don't understand exactly what a user control is; we'll discuss those in a later chapter. For now, just concentrate on the dependency properties we'll create and use. To create the User Control, right-click on the Project node in the Solution Explorer and select Add and then User Control….

  3. In the resulting dialog, type SimpleControl in the Name box, and then click on Add:

  4. We'll add a dependency property to the SimpleControl class. A dependency property needs to be "registered" with the property system. Open the SimpleControl.xaml.cs file and type propdp just after the closing brace of the constructor. This is how it would look in the Visual Studio editor:

  5. This is a code snippet that helps with the (somewhat unpleasant) details of properly registering the property. Press Tab; the code snippet is expanded to something like the following:

    The first part looks like a normal getter/setter of a property (although the implementation is anything but "normal"). The second part actually registers the property with some information (more on that in the How it works… section). Let's create a property named YearPublished of type int.

  6. Press Tab to skip the int part (as that's what we want here). The focus should jump to MyProperty. Type YearPublished as the property name.

  7. Press Tab again. Note that this changes the property name in the lower Register call to YearPublished. The focus should jump to the ownerclass part. Type SimpleControl.

  8. Press Tab again. The focus should jump to the 0. This should be the default value of the property, if not altered. Change the 0 into 2000. After removing the (unhelpful) comment from the snippet provided, the code should look as follows:

    public int YearPublished {
       get { return (int)GetValue(YearPublishedProperty); }
       set { SetValue(YearPublishedProperty, value); }
    }
     
    public static readonly DependencyProperty 
       YearPublishedProperty = DependencyProperty.Register(
          "YearPublished", typeof(int), typeof(SimpleControl),
          new UIPropertyMetadata(2000));
  9. Now let's test it. If that's indeed a dependency property, then there are a few things it can do, such as data bind. We'll add an instance of the SimpleControl class to our main window and bind the property we defined to some other control.

  10. Open the MainWindow.xaml file. Replace the existing Grid with a StackPanel, and add an instance of our SimpleControl. The entire markup should look like as follows:

    <Window x:Class="CH01.DependencyProperties.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="clr-namespace:CH01.DependencyProperties"
            Title="MainWindow" Height="350" Width="525">
        <StackPanel>
            <local:SimpleControl x:Name="_simple" />
        </StackPanel>
    </Window>
  11. Note the local prefix pointing to our .NET namespace (as explained in an earlier recipe of this chapter). Now let's add a TextBlock and a Button inside the StackPanel. The Text property of TextBlock should bind to the YearPublished property of the SimpleControl instance:

    <TextBlock Text="{Binding YearPublished, ElementName=_simple}"
    FontSize="30" />
    <Button Content="Change Value" FontSize="20"/>
  12. When the button is clicked, we'll increment the YearPublished property and see if that changes the displayed text in the TextBlock. First, add a Click event handler; within the Button element type Click=. Visual Studio writes a pair of quotes and suggests adding a handler. You can press Tab to accept the default handler name, or type yourself an appropriate name, such as OnChangeValue:

    <Button Content="Change Value" FontSize="20" Click="OnChangeValue"/>
  13. Right-click on the handler name (OnChangeValue in this case), and select Navigate to Event Handler:

  14. Visual Studio switches to the MainWindow.xaml.cs file inside the event handler. Add a simple increment of the YearPublished property of SimpleControl named _simple. The entire method should look as follows:

    private void OnChangeValue(object sender, RoutedEventArgs e) {
       _simple.YearPublished++;
    }            
  15. Run the application. You should see the TextBlock showing 2000. That's the default value we set in the DependencyProperty.Register call.

  16. Now press the button Change Value a few times— the text should be incremented. This happened because of the change notifications raised by the dependency property system, to which the data binding system registered.

How it works...

A dependency property is managed by a public static (readonly) field named with the property name suffixed with Property; in our case it's MyValueProperty. This field manages the property value for any instance of the type it's declared in. The call to DependencyProperty.Register sets the property's name, its type, the owner type, and set of metadata for that property. The previous code uses an instance of UIPropertyMetadata (one of several possible types), that accepts (at least) the default value for the property (10 in our example).

The classic getter/setter method pair includes calls to SetValue and GetValue. These are defined in the DependencyObject base class, which means any type that wants to leverage the dependency property system must inherit from this class (directly or indirectly). For WPF elements, this is not a problem, as everything inherits from DependencyObject eventually.

When a new value is set for the property (as we did for our code), the SetValue method does "the right thing", meaning (for example), sending notifications to whoever is listening (such as the data binding system).

There's more...

When we register a dependency property, we can provide a property changed callback delegate, to be called when that property value changes for whatever reason:

public static readonly DependencyProperty 
   YearPublishedProperty =   DependencyProperty.Register(
      "YearPublished", typeof(int), typeof(SimpleControl), 
      new UIPropertyMetadata(2000, OnValueChanged));
 
 
private static void OnValueChanged(DependencyObject obj, 
   DependencyPropertyChangedEventArgs e) {
   // do something when property changes
}

We'll examine this technique in a future recipe when we discuss attached properties.

This may seem unnecessary— after all, can't we just add code to the setter, and then just know when the property value is changed? No. The reason is that the property setter is just syntactic sugar—it's not always called directly. It is used by developers, as this is the familiar property syntax, but the XAML parser, for example, just calls SetValue directly—bypassing the property setter altogether. And it's not the only entity doing so. The setter should have nothing in it except the SetValue call.

Property value inheritance

User interface needs to be consistent, usually having the same fonts, sizes, and so on. Setting a font size, for example, on each and every element so they have the same value is tedious and unmaintainable. One of the ways WPF deals with that (not the only way; we'll see another powerful way in Chapter 8) is the idea of propagating a property value down the visual tree. This mechanism, property value inheritance, is supported by the dependency property infrastructure.

A canonical example is the font-related properties. If we set the FontSize property (for instance) on some container element, child elements (in any level) would use that property instead of the default:

<Window x:Class="CH01.InheritDemo.MainWindow"
        Title="MainWindow" FontSize="20" 
        Width="400" Height="300">
    <StackPanel>
        <TextBlock Text="Text 1" />
        <TextBlock Text="Text 2" />
        <TextBlock Text="Text 3" />
    </StackPanel>
</Window>

Note the FontSize is set to 20 on the Window object. All the TextBlocks will now use the value of 20 for their font size instead of the default. This is a feature a dependency property may choose to use, specified at registration time. Here's an example of a dummy property:

public static readonly DependencyProperty DummyProperty =
   DependencyProperty.Register("Dummy", typeof(int),
      typeof(MainWindow), new FrameworkPropertyMetadata(0,
      FrameworkPropertyMetadataOptions.Inherits));

That last flag makes this property inheritable by default (same as the font related properties).

Why "dependency"?

What is the "dependency" part of dependency properties? In the previous section, we looked at visual inheritance. Suppose that one of those example TextBlocks sets a different FontSize values like so:

<Window x:Class="CH01.InheritDemo.MainWindow"
        Title="MainWindow" FontSize="20" 
        Width="400" Height="300">
    <StackPanel>
        <TextBlock Text="Text 1" />
        <TextBlock Text="Text 2" FontSize="30"/>
        <TextBlock Text="Text 3" />
    </StackPanel>
</Window>

What will be the final result? It turns out 30 is the winner for the second TextBlock. What we see is a set of priorities for providers of values. The first (and lowest) priority is the default value registered with DependencyProperty.Register. A higher priority is the inheritance feature (if registered as such for that property). A higher still priority is a local value (30 in our example) that takes precedence over inheritance. So, a dependency property depends on one of several levels or priorities of value providers. In fact, there are about 11 different levels in all (we have seen three in this example). All provider values are not lost—they may become effective if the highest provider is cleared. Here's an example:

_text2.ClearValue(TextBlock.FontSizeProperty);

This clears the local value of the (for example) second TextBlock, reverting its FontSize value to 20 (the inherited value).

By the way, the highest priority provider (except for a coercion callback, explained in the next section) is an active animation. If this wasn't so, an animation would simply have no effect. Once the animation is removed, the property value reverts to its previous state (depending on the highest provider at that time).

We need to take this behavior into consideration. If a property does not seem to get the expected value, there's a good chance we missed some provider that's "stronger" than the one we expected to win. The Visual Studio debugger has a visualizer that can be used to view the current property values of elements, and (very important) the provider that's effectively providing this value. Here's an example for our famous second TextBlock:

Note the Local reading of the Source column.

The CH11.InheritDemo project, available with the downloadable source for this chapter, can be used to test it out. To get to this dialog, set a breakpoint where you have easy access to the required variable (in this case it could be done in the MainWindow constructor after InitializeComponent), and then click on the small magnifying glass near the variable's value column:

If we remove the FontSize="30" local value setting, and use the visualizer again, we get the following:

The Source column clearly indicates that the value was set because of visual inheritance.

This information is also available by using other tools that don't require Visual Studio or a debugger of any kind. One such free tool is Snoop (http://snoopwpf.codeplex.com). This tool can look at any WPF window and drill down into the visual tree, showing property values (with their source); since it does not require anything special, it can be used in production environments, where tools such as Visual Studio are not typically found.

Dependency property levels

As mentioned, there are 11 levels, or priorities, of dependency property providers. Here's the complete list (highest to lowest precedence):

  1. Property coercion: The coercion mechanism allows a delegate to execute before the final value is set for the property. That coercion delegate is provided as part of the property metadata at registration time. For example, if a property signifies an hour in the day, it should have a value between 0 and 23. The coercion callback can look at the suggested value, and if (say) it's greater than 23, return 23 as the final value.

  2. Active animation: If an animation is active, it provides the property's current value.

  3. Local value: Set through the property setter in code, or through XAML.

  4. Template parent properties: If the control was created as part of a ControlTemplate or DataTemplate, these properties apply (we'll discuss data templates in Chapter 6 and control templates in Chapter 8).

  5. Implicit style: (We'll discuss implicit styles in Chapter 8).

  6. Style triggers from Windows or the application (we'll discuss triggers in Chapter 8).

  7. Template triggers: Triggers that are part of a template (again, Chapter 8).

  8. Style setters: Values from styles defined in the Window or the application (styles are discussed in Chapter 8).

  9. Default style: Set by the control creator and can be based on the current Windows theme.

  10. Inheritance: As discussed in a previous section.

  11. Default value: As set in the property metadata.

For a detailed look at all dependency property levels, you can refer to this link in the official MSDN documentation: http://msdn.microsoft.com/en-us/library/1FBADA8E-4867-4ED1-8D97-62C07DAD7EBC(v=vs.100,d=loband).aspx

lock icon The rest of the chapter is locked
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime
Banner background image