Windows Presentation Foundation 4.5 Cookbook
上QQ阅读APP看书,第一时间看更新

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).
    How to do it...
  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:
    How to do it...
  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:
    How to do it...
  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 to do it...

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.