Home Web Development Windows Presentation Foundation 4.5 Cookbook

Windows Presentation Foundation 4.5 Cookbook

By Pavel Yosifovich
books-svg-icon Book
eBook $39.99 $27.98
Print $65.99
Subscription $15.99 $10 p/m for three months
$10 p/m for first 3 months. $15.99 p/m after that. Cancel Anytime!
What do you get with a Packt Subscription?
This book & 7000+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook + Subscription?
Download this book in EPUB and PDF formats, plus a monthly download credit
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook?
Download this book in EPUB and PDF formats
Access this title in our online reader
DRM FREE - Read whenever, wherever and however you want
Online reader with customised display settings for better reading experience
What do you get with video?
Download this video in MP4 format
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with video?
Stream this video
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with Audiobook?
Download a zip folder consisting of audio files (in MP3 Format) along with supplementary PDF
What do you get with Exam Trainer?
Flashcards, Mock exams, Exam Tips, Practice Questions
Access these resources with our interactive certification platform
Mobile compatible-Practice whenever, wherever, however you want
BUY NOW $10 p/m for first 3 months. $15.99 p/m after that. Cancel Anytime!
eBook $39.99 $27.98
Print $65.99
Subscription $15.99 $10 p/m for three months
What do you get with a Packt Subscription?
This book & 7000+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook + Subscription?
Download this book in EPUB and PDF formats, plus a monthly download credit
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook?
Download this book in EPUB and PDF formats
Access this title in our online reader
DRM FREE - Read whenever, wherever and however you want
Online reader with customised display settings for better reading experience
What do you get with video?
Download this video in MP4 format
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with video?
Stream this video
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with Audiobook?
Download a zip folder consisting of audio files (in MP3 Format) along with supplementary PDF
What do you get with Exam Trainer?
Flashcards, Mock exams, Exam Tips, Practice Questions
Access these resources with our interactive certification platform
Mobile compatible-Practice whenever, wherever, however you want
  1. Free Chapter
    Foundations
About this book
Windows Presentation Foundation (WPF) provides developers with a unified programming model for building rich Windows smart client user experiences that incorporate UI, media, and documents.WPF has become the leading technology for developing rich client applications on the Windows platform, packed with features and capabilities. However, WPF is big; in fact, it's huge, causing a steep learning curve for the beginner and even for those already using some WPF features.Windows Presentation Foundation 4.5 Cookbook provides clear recipes for common WPF tasks. It includes detailed explanations and code examples for customizing and enhancing the basic scenarios, while gaining a deep understanding of WPF mechanics and capabilities.WPF is different and requires a different mind-set and approach. This book provides recipes and insights not only in its design but also its practical implementation details.Starting from the foundations of WPF, such as dependency properties and XAML, the book touches on all major WPF aspects, such as controls and layout, resources, and digs deep into its unprecedented data binding capabilities.The book shows data and control templates in action, which allow full customizations of displayed data and controls in a declarative way. Supported by styles and resources makes data binding all the more powerful. The Model View View-Model pattern is presented as an effective way of maximizing decoupling of components, while providing an elegant way of expanding applications while maintaining a tight grip on complexity.The later parts discuss custom elements and controls ñ the ultimate customization mechanism, and looks at multithreading issues, and how .NET 4.5 task parallelism features can enhance application performance.
Publication date:
September 2012
Publisher
Packt
Pages
464
ISBN
9781849686228

 

Chapter 1. Foundations

In this chapter we will cover the following:

  • Creating custom type instances in XAML

  • Creating a dependency property

  • Using an attached property

  • Creating an attached property

  • Accessing a static property in XAML

  • Creating a custom markup extension

  • Handling routed events

 

Introduction


Any attempt at mastering a technology, any technology, requires a good understanding of its foundations. This understanding makes it possible to grasp the more complex aspects of that technology; Windows Presentation Foundation (WPF) is no different.

In this first chapter, we'll discuss recipes concerning the very foundations of WPF – what makes it tick—and also along the way, what makes it unique.

XAML

The first noticeable facet of WPF is XAML (eXtensible Markup Language). XAML is an XML based language used in WPF to declaratively create user interfaces. Actually, XAML has nothing to do with UI. It's merely a declarative way of constructing objects and setting their properties. In fact, it's leveraged in other technologies, such as the Windows Workflow Foundation (WF), where it's used as a way of constructing workflows. To create objects in XAML, they must be "XAML friendly" – meaning they must have the following:

  • A public default constructor

  • Settable public properties

The second item is not strictly a requirement, but the lack of settable properties would make the object a bit dull. Note that starting with .NET 4, XAML is in fact capable of handling parameterized constructors, but WPF's XAML parser currently does not leverage that capability.

XAML is not an absolute requirement. In fact, you can create an entire application using C# or VB (or whichever .NET language you fancy) without a single XAML tag. However, that would be much more difficult and error prone with high maintenance costs, not to mention the difficulty of integration with your fellow designers.

XAML is about the what, not the how. This declarative style makes things easier (granted, after some getting used to), and is a paradigm shift in software development in general (the classic example in .NET being the LINQ-based technologies). XAML is neutral—it's not C# or anything like that—so other, non-developer tools can create or manipulate it. Microsoft provides the Expression Blend tool, which at its core, is a glorified XAML producer/consumer.

XAML and compilation

What happens to a XAML file? How is it tied to the code behind file created by Visual Studio? A XAML file is compiled by a XAML compiler that produces a binary version of the XAML, known as BAML. This BAML is stored as a resource inside the assembly and is parsed at runtime in the InitializeComponent call to create the actual objects. The result is bundled with the code behind file (the typical Window class is declared as partial, meaning there may be more source files describing the same class) to produce the final code for that class.

Browsing a typical WPF application, we won't find the canonical Main method, because it's generated by WPF. It constructs the singleton Application object instance and creates the first window specified by the Application.StartupUri property (if not null). We can find that code in the file App.g.cs (g stands for generated) inside the Obj\x86\Debug sub-folder.

Dependency properties

.NET properties are nothing more than syntactic sugar over set and get methods. What those methods do is up to the property's developer. More often than not, a property is a thin wrapper over a private field, perhaps adding some validation logic in its setter.

WPF requires more out of its properties. Specifically, WPF's dependency properties provide the following:

  • Change notifications when the property's value is changed.

  • Validation handler called as part of a set operation.

  • Coercion handler that is able to "coerce" the provided value to an acceptable value.

  • Various providers can attempt to set the property's value, but only one such provider wins at a time. Nevertheless, all values are retained. If the winning provider goes away, the property's value is set to the next winner in line.

  • Property value inheritance down the visual tree (if so desired).

  • No memory is allocated for a property's value if that value is never changed from its default.

These features provide the basis of some of WPF's strong features, such as data binding and animation.

On the surface, these properties look the same as any other property—a getter and a setter. But no private fields are involved, as we'll see in the following recipes.

 

Creating custom type instances in XAML


Sometimes there's a need to create instances of your own types, or other .NET Framework, non-WPF types within XAML. A classic example is a data binding value converter (which we'll explore in Chapter 6, Data Binding, but other scenarios might call for it).

Getting ready

Make sure you have Visual Studio 2010 up and running.

How to do it...

We'll create a simple application that creates an instance of a custom type in XAML to demonstrate the entire procedure:

  1. Create a new WPF Application project named CH01.CustomTypes.

  2. Let's create a custom type named Book. In the Solution Explorer window, right-click on the project node and select Add and then Class…:

  3. Type Book in the Name box and click on Add:

  4. Add four simple properties to the resulting class:

    class Book {
       public string Name { get; set; }
       public string Author { get; set; }
       public decimal Price { get; set; }
       public int YearPublished { get; set; }
     }

    Tip

    Downloading the example code

    You can download the example code files for all Packt books you have purchased from your account at http://www.PacktPub.com. If you purchased this book elsewhere, you can visit http://www.PacktPub.com/support and register to have the files e-mailed directly to you.

  5. Open the MainWind w.xaml file (using the Solution Explorer), which was created automatically by the project wizard. We would like to create an instance of the Book class. As a Book is not an element (does not derive from UIElement), we cannot simply create it inside our Grid. But, we can make it the Content property (that can be anything, as its type is Object) of a ContentControl-derived type, such as Button. Add a button control to the existing grid, as follows:

    <Grid>
        <Button FontSize="20">
        </Button>    
    </Grid>
  6. To create an instance of Book, we first need to map the .NET namespace (and assembly) where Book is defined to an XML namespace that can be used by the XAML compiler. Let's add a mapping at the top of the XAML near the default mappings added by the application wizard:

    <Window x:Class="CH01.CustomTypes.MainWindow"
       xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
       xmlns:local="clr-namespace:CH01.CustomTypes"
    
  7. This says that anything prefixed by the string local (we can select anything here), should be looked up in the CH01.CustomTypes namespace (in the current assembly).

  8. Now, we can finally create a Book instance. Add the following inside the Button tag:

    <Button FontSize="20">
       <local:Book Name="Windows Internals" 
       Author="Mark Russinovich" Price="40" 
       YearPublished="2009" />
    </Button>    
  9. That's it. We can verify this by adding a suitable ToString implementation to the Book type, and running the application:

    public override string ToString() {
    return string.Format("{0} by {1}\nPublished {2}", Name, 
          Author, earPublished); 
    
    }

How it works...

The XAML compiler needs to be able to resolve type names such as Button or Book. A simple name like Button is not necessarily unique, not in the XML sense and certainly not in the .NET sense (there are at least four Button types in .NET, naturally in diffrent namespaces) .

A mapping is required between an XML namespace and a .NET namespace, so that the XAML compiler can reference the correct type. By default, two XML namespaces are declared by a typical XAML file: the first, which is the default XML namespace, is mapped to the normal WPF namespaces (System.Windows, System.Windows.Controls, and so on). The other, typically with the x prefix, is mapped to the XAML namespace (System.Windows.Markup).

For our own types, we need to do similar mapping (but with a different syntax) means map the XML namespace prefix local to the .NET namespace CH01.CustomTypes. The following line:

xmlns:local="clr-namespace:CH01.CustomTypes"

This allows our Book class to be recognized and used within the XAML.

If the type was defined in a referenced assembly (not our own assembly), then the mapping would continue to something like the following:

xmlns:local="clr-namespace:CH01.CustomTypes;assembly=MyAssembly"

For example, suppose we want the ability to create instances of the System.Random type. Here's how we'd map an XML namespace to the .NET namespace and assembly where Sys em.Random resides:

xmlns:sys="clr-namespace:System;assembly=mscorlib"

Now, we could create an instance of anything in the System namespace (that is XAML friendly) and the mscorlib assembly (such as Random):

 <sys:Random x:Key="rnd" />

In this case, it's hosted in a ResourceDictionary (which is a kind of dictionary, meaning every value requires a key; we'll discuss these in more detail in the next chapter).

There's more...

It's possible to map a single XML namespace to multiple .NET namespaces. This is the same technique used by the WPF assemblies itself: a single XML namespace maps to multiple WPF namespaces, such as System.Windows, System.Windows.Controls, and System.Wind ws.Media.

The trick is to use the XmlnsDefinition attribute within the assembly where the exported types reside. This only works for referenced assemblies; that is, it's typically used in class library assemblies.

For example, suppose we create a MyClassLibrary class library assembly, with a type like the Book introduced earlier:

namespace MyClassLibrary {
   public class Book {
      public string Name { get; set; }
      public string Author { get; set; }
      public decimal Price { get; set; }
      public int YearPublished { get; set; }
   }
}

We can make all types within this assembly and the MyClassLibrary namespace part of an XML namespace by adding the following attribute:

[assembly: XmlnsDefinition("http://mylibrary.com",
"MyClassLibrary")]

The first argument to the attribute is the XML namespace name. This can be anything, but is typically some form of fictitious URL (usually a variation on the company's URL), so as to lower chances of collisions (this is exactly the same idea used by WPF). The second string is the .NET namespace mapped by this XML namespace.

Now, suppose we have another .NET namespace within that same assembly with some types declared within it:

namespace MyClassLibrary.MyOtherTypes {
   public class MyType1 {
      //...
   }
 
   public class MyType2 {
      //...
   }
}

We can add another attribute to map the same XML namespace to the new .NET namespace:

[assembly: XmlnsDefinition("http://mylibrary.com", "MyClassLibrary.MyOtherTypes")]

This means that a single XML prefix (in some client application) can now map to multiple .NET namespaces:

xmlns:mylib="http://mylibrary.com" 

This scheme can save multiple distinct XML prefix declarations. One consequence of this idea is that all public type names must be unique across the mapped .NET namespaces (as they are indeed within WPF itself).

 

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

 

Using an attached property


Attached properties are curious beings. There is no direct analogue to anything else in the .NET framework. The closest may be extension methods, introduced in C# 3.0. Extension methods are a way of extending a type without inheriting from it (even if that type is sealed). Attached properties are dependency properties that are defined by some type, but can be used by (almost) any other typed object. That is, they can extend a type's properties without code derivation. In this recipe, we'll see how to use an existing attached property, and in the next one we'll learn how to create a new attached property.

Getting ready

Make sure Visual Studio is up and running.

How to do it...

We'll create an application that creates a rectangle and places it inside a canvas at exact coordinates using attached properties:

  1. Create a new WPF Application named CH01.SimpleAttached.

  2. Open the MainWindow.xaml file and create the following basic layout:

    <Grid>
       <Canvas>
          <RepeatButton Content="Move" />
       </Canvas>
    </Grid>
  3. This creates a grid which hosts a Canvas and that canvas hosts a RepeatButton. Let's add a Rectangle element to the Canvas and place it in some position:

    <Canvas>
        <RepeatButton Grid.Row="1" Content="Move" />
        <Rectangle x:Name="_rect" Width="50" Height="50" 
                Fill="Red" Stroke="Black" StrokeThickness="5"
                Canvas.Left="30" Canvas.Top="40" />
    </Canvas>
  4. Run the application. You should see something like this:

  5. The Canvas.Left and Canvas.Top are attached properties. They are defined by the Canvas type, but they can be applied to any element (technically, anything that derives from DependencyObject). In this case, these two properties are applied to the Rectangle, essentially "extending" its property set with two new properties. The syntax DefiningClassName.PropertyName is the way to access an attached property in XAML.

  6. Now let's try changing these properties in code. When the repeat button is clicked, let's move the rectangle a little bit to the right. First, let's name the Rectangle, so we can easily refer to it in code:

    <Rectangle x:Name="_rect" Width="50" Height="50" Fill="Red" 
               Stroke="Black" StrokeThickness="5"
               Canvas.Left="30" Canvas.Top="40" />
  7. Add a Click event handler to the RepeatButton. In the handler, add the following code:

       Canvas.SetLeft(_rect, Canvas.GetLeft(_rect) + 5);
  8. An attached property is accessed in code using a set of static methods on the class declaring the property (in this case, the Canvas). The first argument to these methods is the intended target of the property (in this case the Rectangle). Run the application. Click on the button (and hold); you should see the rectangle moving along to the right, 5 units at a time.

How it works...

An attached property is first and foremost a dependency property, meaning it supports all the capabilities of dependency properties. However, as an attached property is "attached" to an object that did not define it, a simple property like syntax is not possible – as C# does not support the concept of attached properties natively. Instead, the declaring class provides two static methods, named DeclaringType.SetPropertyName and DeclaringType.GetPropertyName, that provide a way to set or get the property value for some object passed in as the first argument (as demonstrated in the last code snippet).

In XAML, things are simpler, as the XAML parser is aware of attached properties, and converts the simpler DeclaringType.PropertyName attribute to the aforementioned Set method.

There's more...

The actual implementation of the Set/Get static methods mentioned above is to call the regular DependencyObject.SetValue/GetValue as for a regular dependency property. This means that the code to move the rectangle could have been written as follows:

_rect.SetValue(Canvas.LeftProperty,
   (double)_rect.GetValue(Canvas.LeftProperty) + 5);

Why an attached property?

One may wonder why to go to all this trouble for the Left and Top properties. Would it not be simpler to define the Left and Top properties on the (for example) UIElement class and be done with it? These properties could have been normal dependency properties and enjoy the simpler syntax they carry.

The reason is, that a Left or Top property may not always make sense. In fact, it only makes sense when the element is placed within a Canvas. What if the rectangle is inside a Grid? Or a StackPanel? The Left/Top properties wouldn't make sense. This leads to the conclusion that attached properties are a kind of contextual property – they are relevant under particular circumstances, so they can be "attached" if and when actually needed.

Does the declaring type "own" the property?

The previous example may lead to a wrong conclusion. It seems Canvas.Left and the like are only relevant when the element is inside a Canvas. Similarly, the Grid.Row and Grid.Column attached properties only make sense for elements placed inside a Grid. Is this somehow necessary from an attached property point of view?

Not at all. This is just coincidence. The above properties in fact make sense only for elements placed inside their respective declaring type, but that does not have to be the case. For example, suppose we have a button with a tool tip defined:

<Button Content="Copy" ToolTip="Copy the Selected Items" />

If the button is disabled (IsEnabled set to true), the tool tip does not appear at runtime. To make it appear even if the control is disabled, we must set the ToolTipService.ShowOnDisabled attached property to true:

<Button Content="Copy" ToolTip="Copy the Selected Items" 
        ToolTipService.ShowOnDisabled="True"/>

We set the property on the button, but it's defined in the ToolTipService class. This class is not an element (unlike the Canvas for example). In fact, it's a static class (instances of it cannot be created). So, there is no relationship between the button and the ToolTipService class, or between the ToolTip and ToolTipService classes, for that matter. The way this connection is established (so it can have some effect) will be revealed in the next recipe in this chapter.

See also

To create your own attached properties, refer to the next recipe, Creating an attached property.

 

Creating an attached property


An attached property can be used to somehow "enhance" or extend another object. In the case outlined in the previous recipe, Using an attached property, an element was placed at exact coordinates within a Canvas using the attached Canvas.Left and Canvas.Top properties. An attached property is a powerful tool for extending the behavior of any object without the need to inherit from the type of the object. In this task, we'll see this in action.

Getting ready

Make sure Visual Studio is up and running.

How to do it...

We'll create an attached property that would rotate any element it's attached to:

  1. Create a new WPF Application named CH01.CustomAttached.

  2. Open MainWindow.xaml. Add some elements in a Canvas (replace the default Grid) as follows:

    <Canvas>
       <Ellipse Fill="Red" Width="100" Height="60" />
       <Rectangle Fill="Blue" Width="80" Height="80"
                  Canvas.Left="100" Canvas.Top="100" />
       <Button Content="Hello" Canvas.Left="130" Canvas.Top="30" 
        FontSize="20" />
    </Canvas>
  3. Suppose we want to rotate a particular element around its center. We would have to write something like this (example for the Ellipse):

    <Ellipse Fill="Red" Width="100" Height="60" 
             RenderTransformOrigin=".5,.5">
       <Ellipse.RenderTransform>
          <RotateTransform Angle="30" />
       </Ellipse.RenderTransform>
    </Ellipse>

    Although this is certainly possible, this makes for a lot of typing. Now imagine doing something similar for other elements. Let's make it shorter by defining and using an attached property.

  4. Add a new class to the project named RotationManager.

  5. We'll register a new attached property within this class; a property any other object can use. To do that, we'll take advantage of a Visual Studio code snippet, propa (similar in concept to propdp discussed in the task Creating a dependency property in this chapter). Inside the class definition, type propa (without the quotes). This is how it should look at this point:

  6. Press Tab once, and fill in the property details as follows: the property type should be double, its name should be Angle, its owner class RotationManager, and its default value zero. You'll have to add a using statement for System.Windows namespace. The generated code should look as follows (after removing the comment and some formatting):

    class RotationManager : DependencyObject {
       public static double GetAngle(DependencyObject obj) {
          return (double)obj.GetValue(AngleProperty);
       }
       public static void SetAngle(DependencyObject obj,
          double value) {
          obj.SetValue(AngleProperty, value);
       }
     
       public static readonly DependencyProperty AngleProperty =
             DependencyProperty.RegisterAttached("Angle",
             typeof(double), typeof(RotationManager),
             new UIPropertyMetadata(0.0));
     
    }
  7. Now that we have an attached property definition, let's use it. We'll set it on our various elements. The first step is mapping an XML namespace to our namespace (as we learned in the recipe Creating custom type instances in XAML in this chapter). Open MainWindow.xaml and add a mapping on the root element, as in the following code snippet:

    xmlns:local="clr-namespace:CH01.CustomAttached"
  8. Now let's set the property with various values on the various elements. Here's an example for the Ellipse (notice the intellisense popping up to help):

     <Ellipse Fill="Red" Width="100" Height="60" 
              local:RotationManager.Angle="45"/>
    
  9. Add similar settings for the Rectangle and Button like as follows:

    <Rectangle Fill="Blue" Width="80" Height="80"
               Canvas.Left="100" Canvas.Top="100"
               local:RotationManager.Angle="30" />
    <Button Content="Hello" Canvas.Left="130" Canvas.Top="30" 
            FontSize="20" 
            local:RotationManager.Angle="90"/>
    
  10. Notice that the designer preview shows no change. If you run the application, nothing happens. And why would anything happen? We declared a property and nothing else. Let's add some behavior logic if the property is actually used. For that, we'll add a property changed handler notification. Go back to RotationManager.cs and modify the property registration as follows:

    public static readonly DependencyProperty AngleProperty =
          DependencyProperty.RegisterAttached("Angle",
          typeof(double), typeof(RotationManager),
          new UIPropertyMetadata(0.0, OnAngleChanged));
  11. The OnAngleChanged method will be called for any change in the property value on any object it's applied to. Let's add some simple logic that will rotate the element:

    private static void OnAngleChanged(DependencyObject obj, 
       DependencyPropertyChangedEventArgs e) {
       var element = obj as UIElement;
       if(element != null) {
          element.RenderTransformOrigin = new Point(.5, .5);
          element.RenderTransform = new RotateTransform(
          (double)e.NewValue);
       }
    }
  12. If we switch back to the designer, we'll see the elements rotated according to the specified angles. If we run the application, we'll see something like this:

There we have it. An easy way to rotate any element by using an attached property.

How it works...

Attached properties are registered similarly to regular dependency properties. In terms of functionality, they are dependency properties. This means they support everything a dependency property supports: data binding, animation, and so on. An attached property can be defined by any class (RotationManager in our example) and can be applied to any object whose type derives from DependencyObject.

Simply registering an attached property has no effect on its own. There must be some "extra" code that looks for that property and does something when it's applied or changed. In the example shown, this is done by specifying a property changed handler, called by WPF whenever the property is changed on any object. In the example code, we restrict using a UIElement-derived type, as this is the first type that supports RenderTransform and RenderTransformOrigin. This also shows the weakness of attached properties: it's not possible to know whether specifying the property on some object is beneficial. We could have thrown an exception if the object was not UIElement-derived to somewhat rectify this (albeit at runtime rather than compile time), but this is not typically employed (although we could have written something with Debug.WriteLine to indicate this needs attention), as there may be other code that does not consider this an invalid setting.

There's more...

The property change notification scheme is typically used by WPF with attached properties that are defined by panels, such as Canvas, DockPanel, and Grid. Note that the panels only look for the relevant attached properties on their immediate children (and not grandchildren). This is not a limitation of attached properties, it's simply the way these panels work. Although attached properties within panels are common, there are other ways these properties can be used.

One possibility is to use the existence of these property values within styles (a complete treatment of styles is in given Chapter 8) or templates (templates are discussed in Chapter 6 and Chapter 8). For now, think of a style as a grouping of related settings that can be applied as a group to an element. For example, the following style accomplishes roughly the same thing as our property change handler:

<Style TargetType="Button">
   <Setter Property="RenderTransformOrigin" Value=".5,.5" />
   <Setter Property="RenderTransform">
      <Setter.Value>
         <RotateTransform Angle="{Binding Path=(local:RotationManager.Angle), RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Button}}" />
      </Setter.Value>
   </Setter>
</Style>

Of course, this example works with buttons only because of the targeted style (style that works on buttons only), but the result is the same. Note the parentheses around the attached property name. This is essential – otherwise the XAML parser does not understand this to be an attached property; it interprets local:RotationManager as the property name (and expects Angle to be a sub-property). Also, leaving out the "Path=" (as is customary in binding expressions), causes the expression to fail (for a similar reason).

Reusing existing attached properties

An attached property is (paradoxically) a detached entity. It has no special affinity to the declaring type. This means we can use an already defined attached property if it's typed appropriately, named appropriately, and has no use in the needed situation. In our example, we need an attached property that is of type double, has an intuitive enough name (maybe something with "angle" or "rotate"), and is unused in scenarios where the use of our property makes sense.

Clearly, it's not easy finding such a property, but sometimes one may get lucky. For instance, if we elect to go for the attached property ToolTipService.HorizontalOffset (typed as double), we can achieve the same effect as previously (with a style setter) without defining a new attached property. This is not a good choice in this case, as an offset is not an angle, and clearly tooltips have nothing to do with rotation. The worse problem here is that there may be a legitimate reason to place that property on a button (to cater offsetting a tooltip), so that reusing for rotation purposes would collide with the tooltip, making only one a winner. Still, the general concept holds – any attached property can be reused.

Attached property reuse is possible in styles, templates (data template and control template), and triggers (within styles and templates).

See also

For background on dependency properties, check out the recipe Creating a dependency property in this chapter.

 

Accessing a static property from XAML


XAML provides an easy way to set values of properties—type converters and the extended property syntax allow for flexible setting of values. However, some things cannot be expressed as a simple value, such as setting a property to the value of some static property.

Getting ready

Make sure Visual Studio is up and running.

How to do it...

We'll create an application that uses a static property from within XAML:

  1. Create a new WPF Application named CH01.StaticProperties.

  2. Open MainWindow.xaml. Replace the Grid element with a StackPanel.

  3. Add some shapes as shown in the following code block:

        <StackPanel>
            <Ellipse Stroke="Black" Height="50" />
            <Rectangle Stroke="Black" Height="50" />
        </StackPanel>
  4. Suppose we want to fill the ellipse with the desktop color selected by the user in Windows. WPF provides the SystemColors class, with many static properties that return a Brush representing the user's choice. For the desktop, this is a DesktopBrush property. We can try the following:

    <Ellipse Stroke="Black" Height="50" 
    Fill="SystemColors.DesktopBrush" />
    
  5. This throws an exception at runtime, as it cannot be converted to any "known" color (such as Red or Blue). To access a static property, we must use the {x:Static} markup extension, as follows:

    <Ellipse Stroke="Black" Height="50" 
    Fill="{x:Static SystemColors.DesktopBrush}" />
    
  6. This works. You can verify this by going to Control Panel, then selecting Personalization (on Windows 7 or 8).

  7. Select Window Color (switch to the classic theme first if the following window is shown differently). The Window Color and Appearance dialog is displayed:

  8. Change the desktop color and run the application again. You should see the ellipse filled with the new color.

  9. Similarly, let's fill the rectangle with the active window caption color:

    <Rectangle Stroke="Black" Height="50" 
    Fill="{x:Static SystemColors.ActiveCaptionBrush}"/>
    
  10. Running the application shows something like the following:

  11. In this case, the active caption color on my system is a gradient, so the ActiveCaptionBrush provides the left side. The right side is provided by the GradientActiveCaptionBrush property. They are both brushes. If we wanted to recreate the caption gradient within the rectangle, we would need color objects, not brushes. Fortunately, these are provided via properties in the same class, named ActiveCaptionColor and GradientActiveCaptionColor. Let's combine these in a LinearGradientBrush:

    <Rectangle Stroke="Black" Height="50">
       <Rectangle.Fill>
          <LinearGradientBrush EndPoint="1,0">
             <GradientStop Offset="0" 
    Color="{x:Static SystemColors.ActiveCaptionColor}" />
             <GradientStop Offset="1" 
    Color="{x:Static SystemColors.GradientActiveCaptionColor}" />
          </LinearGradientBrush>
       </Rectangle.Fill>
    </Rectangle>
  12. This is the final result:

How it works...

XAML basically has very few capabilities. It can create objects, set values for properties, and set up event handlers. This is intentional, as XAML is declarative in nature. It cannot, for instance, call methods. That would make it closer to an imperative language (such as C#), which would make its existence dubious at best.

Sometimes, however, declarative operations require more than setting up properties. A method may be involved, or some other unusual construct, but the intent may still be declarative. This is where markup extensions come in. They provide a way to extend XAML with new (hopefully declarative) capabilities.

A markup extension is a class that derives from System.Windows.Markup.MarkupExtension and implements a single method, ProvideValue. In this example we have used the {x:Static} markup extension, which allows accessing any static property (whether it belongs to WPF or not; if not, a XAML namespace mapping is required as explained in the recipe Creating custom type instances in XAML in this chapter). {x:Static} is implemented by the System.Windows.Markup.StaticExtension class. Note that if the markup extension class ends with "Extension" we can remove it when referring to it in XAML – the XAML compiler will search with and without the word "Extension". This means {x:Static} can be written {x:StaticExtension}. In fact, most markup extensions end with "Extension" (a notable exception is the Binding markup extension).

There's more...

There are other built in markup extensions. Here are some of the simplest:

  • {x:Null} specifies the null reference

  • {x:Type SomeType} is the equivalent to the typeof(SomeType) operator in C#

We'll look at other markup extensions in subsequent chapters.

 

Creating a custom markup extension


Markup extensions are used to extend the capabilities of XAML, by providing declarative operations that need more than just setting some properties. These can be used to do pretty much anything, so caution is advised – these extensions must preserve the declarative nature of XAML, so that non-declarative operations are avoided; these should be handled by normal C# code.

Getting ready

Make sure Visual Studio is up and running.

How to do it...

We'll create a new markup extension that would provide random numbers and use it within a simple application:

  1. First, we'll create a class library with the markup extension implementation and then test it in a normal WPF application. Create a new Class Library project named CH01.CustomMarkupExtension. Make sure the checkbox Create directory for solution is checked, and click on OK:

  2. The base MarkupExtension class resides in the System.Xaml assembly. Add a reference to that assembly by right-clicking the References node in the Solution Explorer, and selecting Add Reference…. Scroll down to System.Xaml and select it.

  3. Delete the file Class1.cs that was created by the wizard.

  4. Right-click the project node, and select Add Class…. Name the class RandomExtension and click on Add. This markup extension will generate a random number in a given range.

  5. Mark the class as public and inherit from MarkupExtension.

  6. Add a using statement to System.Windows.Markup or place the caret somewhere over MarkupExtension, click on the smart tag (or press Ctrl + . (dot), and allow the smart tag to add the using statement for you. This is how the class should look right now:

       public class RandomExtension : MarkupExtension {}
  7. We need to implement the ProvideValue method. The easiest way to get the basic prototype is to place the caret over MarkupExtension and use the smart tag again, this time selecting Implement abstract class. This is the result:

    public class RandomExtension : MarkupExtension {
       public override object ProvideValue(IServiceProvider sp) {
          throw new NotImplementedException();
       }
    }
  8. Before we create the actual implementation, let's add some fields and constructors:

    readonly int _from, _to; 
    public RandomExtension(int from, int to) {
       _from = from; _to = to;
    } 
    public RandomExtension(int to)
       : this(0, to) {
    }
  9. Now we must implement ProvideValue. This should be the return value of the markup extension – a random number in the range provided by the constructors. Let's create a simple implementation:

    static readonly Random _rnd = new Random();
    public override object ProvideValue(IServiceProvider sp) {
       return (double)_rnd.Next(_from, _to);
    }
  10. Let's test this. Right-click on the solution node in Solution Explorer and select Add and then New Project….

  11. Create a WPF Application project named CH01.TestRandom.

  12. Add a reference to the class library just created.

  13. Open MainWindow.xaml. We need to map an XML namespace to the namespace and assembly our RandomExtension resides in:

    xmlns:mext="clr-namespace:CH01.CustomMarkupExtension;
    assembly=CH01.CustomMarkupExtension"
  14. Replace the Grid with a StackPanel and a couple of TextBlocks as follows:

    <StackPanel>
       <TextBlock FontSize="{mext:Random 10, 100}" Text="Hello"
                  x:Name="text1"/>
       <TextBlock Text="{Binding FontSize, ElementName=text1}" />
    </StackPanel>
  15. he result is a TextBlock that uses a random font size between 10 and 100. The second TextBlock shows the generated random value.

How it works...

A markup extension is a class inheriting from MarkupExtension, providing some service that cannot be done with a simple property setter. Such a class needs to implement one method: ProvideValue. Whatever is returned provides the value for the property. ProvideValue accepts an IServiceProvider interface that allows getting some "context" around the markup extension execution. In our simple example, it wasn't used.

Any required arguments are passed via constructor(s). Any optional arguments can be passed by using public properties (as the next section demonstrates).

Let's try using our markup extension on a different property:

 <TextBlock Text="{mext:Random 1000}" />

We hit an exception. The reason is that our ProvideValue returns a double, but the Text property expects a string. We need to make it a bit more flexible. We can query for the expected type and act accordingly. This is one such service provided through IServiceProvider:

public override object ProvideValue(IServiceProvider sp) {
   int value = _rnd.Next(_from, _to);
   Type targetType = null;
   if(sp != null) {
      var target = sp.GetService(typeof(IProvideValueTarget)) 
         as IProvideValueTarget;
      if(target != null) {
         var clrProp = target.TargetProperty as PropertyInfo;
         if(clrProp != null)
            targetType = clrProp.PropertyType;
         if(targetType == null) {
            var dp = target.TargetProperty 
               as DependencyProperty;
            if(dp != null)
               targetType = dp.PropertyType;
         }
      }
   }
   return targetType != null ?
      Convert.ChangeType(value, targetType) :
      value.ToString();
}

You'll need to add a reference for the WindowsBase assembly (where DependencyProperty is defined). IServiceProvider is a standard .NET interface that is a kind of "gateway" to other interfaces. Here we're using IProvideValueTarget, which enables discovering what property type is expected, with the TargetProperty property. This is either a PropertyInfo (for a regular CLR property) or a DependencyProperty, so appropriate checks must be made before the final target type is ascertained. Once we know the type, we'll try to convert to it automatically using the Convert class, or return it as a string if that's not possible.

For more information on other interfaces that can be obtained from this IServiceProvider, check this page on the MSDN documentation: http://msdn.microsoft.com/en-us/library/B4DAD00F-03DA-4579-A4E9-D8D72D2CCBCE(v=vs.100,d=loband).aspx.

There's more...

Constructors are one way to get parameters for a markup extension. Properties are another, allowing optional values to be used if necessary. For example, let's extend our random extension, so that it is able to provide fractional values and not just integral ones. This option would be set using a simple public property:

   public bool UseFractions { get; set; }

The implementation of ProvideValue should change slightly; specifically, calculation of the value variable:

   double value = UseFractions ? 
      _rnd.NextDouble() * (_to - _from) + _from : 
      (double)_rnd.Next(_from, _to);

To use it, we set the property after the mandatory arguments to the constructor:

 <TextBlock Text="{mext:Random 1000, UseFractions=true}" />

Don't go overboard

Markup extensions are powerful. They allow arbitrary code to run in the midst of XAML processing. We just need to remember that XAML is, and should remain, declarative. It's pretty easy to go overboard, crossing that fine line. Here's an example: let's extend our RandomExtension to allow modifying the property value at a regular interval. First, a property to expose the capability:

   public TimeSpan UpdateInterval { get; set; }

Now, some modifications to the ProvideValue implementation:

if(UpdateInterval != TimeSpan.Zero) {
   // setup timer...
   var timer = new DispatcherTimer();
   timer.Interval = UpdateInterval;
   timer.Tick += (sender, e) => {
      value = UseFractions ?
         _rnd.NextDouble() * (_to - _from) + _from :
         (double)_rnd.Next(_from, _to);
      finalValue = targetType != null ?
         Convert.ChangeType(value, targetType) :
         value.ToString();
      if(dp != null)
         ((DependencyObject)targetObject).SetValue(
            dp, finalValue);
      else if(pi != null)
         pi.SetValue(targetObject, value, null);
   };
   timer.Start();
}

targetObject is obtained by calling IProvideValueTarget.TargetObject. This is the actual object on which the property is to be set.

And the markup:

<TextBlock Text="This is funny" 
FontSize="{mext:Random 10, 50, UpdateInterval=0:0:1}" />

This is certainly possible (and maybe fun), but it's probably crossing the line.

 

Handling routed events


Events are essentially notifications from an object to the outside world – a variation on the "observer" design pattern. Most of the time an object is told what to do via properties and methods. Events are its way of talking back to whoever is interested. The concept of events existed in .NET since its inception, but WPF has something to say about the way events are implemented. WPF introduces routed events, an enhanced infrastructure for raising and handling events, which we'll look at in this recipe.

Getting ready

Make sure Visual Studio is up and running.

How to do it...

We'll create a simple drawing application that uses routed events to handle user interaction:

  1. Create a new WPF Application named CH01.SimpleDraw. This will be a simple drawing program.

  2. Add some markup to MainWindows.xaml that includes a Canvas and some rectangle objects to select drawing brushes:

    <Canvas Background="White" Name="_root">
    </Canvas>
  3. To do some drawing, we'll handle the MouseLeftButtonDown, MouseMove, and MouseUp events on the canvas object. Within the Canvas tag, type MouseLeftButtonDown=. Intellisense will pop up, suggesting to add a default handler name. Resist the temptation, and type OnMouseDown:

    <Canvas Background="White" Name="_root" 
        MouseLeftButtonDown="OnMouseDown">
    
  4. Right-click on OnMouseDown and select Navigate to Event Handler. Visual Studio will add the required handler method in the code behind file (MainWindow.xaml.cs) and jump straight to it:

    private void OnMouseDown(object sender, 
       MouseButtonEventArgs e) {
    }
  5. Add similar handlers for the MouseMove and MouseUp events, named OnMouseMove and OnMouseUp, respectively.

  6. Let's add simple drawing logic. First, add the following fields to the MainWindow class:

          Point _pos;
          bool _isDrawing;
          Brush _stroke = Brushes.Black;
  7. Now the OnMouseDown event handler:

    void OnMouseDown(object sender, MouseButtonEventArgs e) {
       _isDrawing = true;
       _pos = e.GetPosition(_root);
       _root.CaptureMouse();
    }
  8. Next, we'll handle mouse movement, like in the following code snippet:

    void OnMouseMove(object sender, MouseEventArgs e) {
       if(_isDrawing) {
          Line line = new Line();
          line.X1 = _pos.X;
          line.Y1 = _pos.Y;
          _pos = e.GetPosition(_root);
          line.X2 = _pos.X;
          line.Y2 = _pos.Y;
          line.Stroke = _stroke;
          line.StrokeThickness = 1;
          _root.Children.Add(line);
       }
    }
  9. If we're in drawing mode, we create a Line object, set its two points locations and add it to the Canvas.

  10. Finally, when the mouse button is released, just revert things to normal:

    void OnMouseUp(object sender, MouseButtonEventArgs e) {
       _isDrawing = false;
       _root.ReleaseMouseCapture();
    }
  11. Run the application. We now have a functional little drawing program. Event handling seemed to be as simple as expected.

  12. Let's make it a little more interesting, with the ability to change drawing color. We'll add some rectangle elements in the upper part of the canvas. Clicking any of them should change the drawing brushing from that point on. First, the rectangles:

       <Rectangle Stroke="Black" Width="25" Height="25" 
                  Canvas.Left="5" Canvas.Top="5" Fill="Red" />
       <Rectangle Stroke="Black"  Width="25" Height="25" 
                  Canvas.Left="35" Canvas.Top="5" Fill="Blue" />
       <Rectangle Stroke="Black" Width="25" Height="25" 
                  Canvas.Left="65" Canvas.Top="5" Fill="Yellow" />
       <Rectangle Stroke="Black" Width="25" Height="25" 
                  Canvas.Left="95" Canvas.Top="5" Fill="Green" />
       <Rectangle Stroke="Black" Width="25" Height="25" 
                  Canvas.Left="125" Canvas.Top="5" Fill="Black" />
  13. How should we handle clicks on the rectangles? One obvious way is to attach an event handler to each and every rectangle. But that would we wasteful. Events such as MouseLeftButtonDown "bubble up" the visual tree and can be handled at any level. In this case, we'll just add code to the OnMouseDown method:

          void OnMouseDown(object sender, MouseButtonEventArgs e) {
          var rect = e.Source as Rectangle;
          if(rect != null) {
             _stroke = rect.Fill;
          }
          else {
             _isDrawing = true;
             _pos = e.GetPosition(_root);
             _root.CaptureMouse();
          }
       }
  14. Run the application and click the rectangles to change colors. Draw something nice.

How it works...

WPF events are called routed events because most can be handled by elements that are not the source of the event. In the preceding example, the MouseLeftButtonDown was handled on the Canvas element, even though the actual event may have triggered on a particular Rectangle element. This is referred to as a routing strategy of bubbling.

When the left mouse button is pressed, we make a note that the drawing has started by setting _isDrawing to true (step 7). Then, we record the current mouse position relative to the canvas (_root) by calling the MouseButtonEventArgs.GetPosition method. And finally, although not strictly required, we "capture" the mouse, so that subsequent events will be sent to the Canvas and not any other window, even if the mouse pointer technically is not over the Canvas.

To properly ascertain which element was actually the source of the event, the RoutedEventArgs.Source property should be used (and not the sender, in our example the sender is always the Canvas).

There's more...

Bubbling is not the only routing strategy WPF supports. The opposite of bubbling is called tunneling; events with a tunneling strategy are raised first on the top level element (typically a Window), and then on its child, and so on, towards the element that is the actual source of the event. After the tunneling event has finished (calling any handlers along the way), its bubbling counterpart is raised, from the source up the visual tree towards the top level element (window).

A tunneling event always has its name starting with Preview. Therefore, there is PreviewMouseLeftButtonDown and its bubbling counterpart is simply MouseLeftButtonDown.

A third routing strategy is supported, called Direct. This is the simplest strategy; the event is raised on the source element of the event and that's it. No bubbling or tunnelling occurs. By the way, only very few events use the Direct strategy (for example, MouseEnter and MouseLeave).

Stopping bubbling or tunneling

After a bubbling event is handled by some element – it continues to bubble. The bubbling can be stopped by setting the RoutedEventArgs.Handled property to true.

If the event is a tunneling one – setting Handled to true stops the tunneling, but it also prevents the buddy-bubbling event from ever firing.

Attached events

Suppose we want to write a simple calculator application:

This is a Grid that contains various Button controls.

We would like to use as few handlers as we can. For the "=" button, we can attach a specific handler and prevent further bubbling:

void OnCalculate(object sender, RoutedEventArgs e) {
   // do operation
   e.Handled = true;

}

What about the digit buttons? Again, we could add a click handler to each one, but that would be wasteful. A better approach would be to leverage the Click event's bubbling strategy and set a single handler on the container Grid.

Typing "Click=" on the Grid tag seems to fail. Intellisense won't help and in fact this won't compile. It may be obvious – a Grid has no Click event. Click is specific to buttons. Does this mean we can't set a Click handler on the Grid? Fortunately, we can.

WPF provides the notion of attached events. Such events can be handled by any element, even if that element's type does not define any such event. This is achieved through attached event syntax (similar to attached properties), such as the following code snippet:

<Grid ButtonBase.Click="OnKeyPressed">

The Click event is defined on the ButtonBase class, although Button.Click works just as well, because Button inherits from ButtonBase. Now we can look at the actual source of the click with the same RoutedEventArgs.Source described previously:

int digit;
string content = ((Button)e.Source).Content.ToString();
if(int.TryParse(content, out digit)) {
   // a digit
}

You can find the complete calculator sample in the CH01.Calculator project, available with the downloadable source for this chapter.

About the Author
  • Pavel Yosifovich

    Pavel Yosifovich is the CTO of CodeValue (http://www.codevalue.net), a software development, consulting, and training company, based in Israel. He writes, develops, consults, and trains developers on various software development topics, from Windows internals to .NET enterprise systems, and almost everything in between. He's a Microsoft MVP and a frequent speaker at national events. In the past, he co-founded the startup company Quiksee that was acquired by Google in September 2010.

    Browse publications by this author
Latest Reviews (6 reviews total)
plagaristic, cut and pasta
great way to learn wpf!!!
Great book for experienced developers which is exactly what I was looking for.
Windows Presentation Foundation 4.5 Cookbook
Unlock this book and the full library FREE for 7 days
Start now