MCTS Self-Paced Training: Styles and Animation in Windows Presentation Foundation

  • 7/9/2008

Understanding Property Value Precedence

By now, you have probably noticed that a property can be set in many different ways. They can be set in code; they can be set by styles; they can have default values; and so on. It might seem logical at first to believe that a property will have the value it was last set to, but this is actually incorrect. There is a defined and strict order of precedence that determines a property’s value based on how it was set, not when. The precedence order is summarized here, with highest precedence listed first:

  1. Set by coercion by the property system.

  2. Set by active animations or held animations.

  3. Set locally, either by code, by direct setting in XAML, or through data binding.

  4. Set by the TemplatedParent. Within this category, there is a sub-order of precedence, again listed in descending order:

    1. Set by Triggers from the templated parent

    2. Set by the templated parent through property sets

  5. Implicit style—this applies only to the Style property.

  6. Set by Style triggers.

  7. Set by Template triggers.

  8. Set by Style setters.

  9. Set by the default Style. There is a sub-order within this category, again listed in descending order:

    1. Set by Triggers in the default style

    2. Set by Setters in the default style

  10. Set by inheritance.

  11. Set by metadata.

This may seem like a complicated and arbitrary order of precedence, but upon closer examination it is actually very logical and based upon the needs of the application and the user. The highest precedence is property coercion. This takes place in some elements if an attempt is made to set a property beyond its allowed values. For example, if an attempt is made to set the Value property of a Slider control to a value higher than the Maximum property, the Value is coerced to equal the Maximum property. Next in precedence come animations. For animations to have any meaningful use, they must be able to override preset property values. The next highest level of precedence is properties that have been set explicitly through developer or user action.

Properties set by the TemplatedParent are next in the order of precedence. These are properties set on objects that come into being through a template. Templates are discussed further in Chapter 8, “Customizing the User Interface.” After this comes a special precedence item that applies only to the Style property of an element: Provided that the Style property has not been set by any item with a higher-level precedence, it is set to a Style whose TargetType property matches the type of the element in question. Then come properties set by Triggers—first those set by a Style, then those set by a Template. This is logical because for triggers to have any meaningful effect, they must override properties set by styles.

Properties set by styles come next: first properties set by user-defined styles, and then properties set by the default style (also called the Theme, which typically is set by the operating system). Finally come properties that are set through inheritance and the application of metadata.

For developers, there are a few important implications that are not intuitively obvious. The most important is that if you set a property explicitly—whether in XAML or in code—the explicitly set property blocks any changes dictated by a Style or Trigger. WPF assumes that you want that property value to be there for a reason and does not allow it to be set by a Style or Trigger, although it still can be overridden by an active animation.

A second, less obvious implication is that when using the Visual Studio designer to drag and drop items onto the design surface from the ToolBox, the designer explicitly sets several properties, especially layout properties. These property settings have the same precedence as they would if you had set them yourself. So if you are designing a style-oriented user interface, you should either enter XAML code directly in XAML view to create controls and set as few properties explicitly as possible, or you should review the XAML that Visual Studio generates and delete settings as appropriate.

You can clear a property value that has been set in XAML or code manually by calling the DependencyObject.ClearValue method. The following code example demonstrates how to clear the value of the Width property on a button named Button1:

' VB
Button1.ClearValue(WidthProperty)

// C#
Button1.ClearValue(WidthProperty);

Once the value has been cleared, it can be reset automatically by the property system.

Lab: Creating High-Contrast Styles

In this lab, you create a rudimentary high-contrast Style for Button, TextBox, and Label elements.

Exercise 1: Using Styles to Create High-Contrast Elements

  1. Create a new WPF application in Visual Studio.

  2. In XAML view, just above the <Grid> declaration, create a Window.Resources section, as shown here:

    <Window.Resources>
    
    </Window.Resources>
  3. In the Window.Resources section, create a high-contrast Style for TextBox controls that sets the background color to Black and the foreground to White. The TextBox controls also should be slightly larger by default. An example is shown here:

    <Style TargetType="TextBox">
       <Setter Property="Background" Value="Black" />
       <Setter Property="Foreground" Value="White" />
       <Setter Property="BorderBrush" Value="White" />
       <Setter Property="Width" Value="135" />
       <Setter Property="Height" Value="30" />
    </Style>
  4. Create similar styles for Button and Label, as shown here:

    <Style TargetType="Label">
       <Setter Property="Background" Value="Black" />
       <Setter Property="Foreground" Value="White" />
       <Setter Property="Width" Value="135" />
       <Setter Property="Height" Value="33" />
    </Style>
    <Style TargetType="Button">
       <Setter Property="Background" Value="Black" />
       <Setter Property="Foreground" Value="White" />
       <Setter Property="Width" Value="135" />
       <Setter Property="Height" Value="30" />
    </Style>
  5. Type the following in XAML view. Note that you should not add controls from the toolbox because that automatically sets some properties in the designer at a higher property precedence than styles:

    <Label Margin="26,62,126,0" VerticalAlignment="Top">
       High-Contrast Label</Label>
    <TextBox Margin="26,117,126,115">High-Contrast TextBox
       </TextBox>
    <Button Margin="26,0,126,62" VerticalAlignment="Bottom">
       High-Contrast Button</Button>
  6. Press F5 to build and run your application. Note that while the behavior of these controls is unaltered, their appearance has changed.

Exercise 2: Using Triggers to Enhance Visibility

  1. In XAML view for the solution you completed in Exercise 1, add a Style.Triggers section to the TextBox Style, as shown here:

    <Style.Triggers>
    
    </Style.Triggers>
  2. In the Style.Triggers section, add Triggers that detect when the mouse is over the control and enlarge the FontSize of the control, as shown here:

    <Trigger Property="IsMouseOver" Value="True">
       <Setter Property="FontSize" Value="20" />
    </Trigger>
  3. Add similar Style.Triggers collections to your other two styles.

  4. Press F5 to build and run your application. The FontSize of a control now increases when you move the mouse over it.

Lesson Summary

  • Styles allow you to define consistent visual styles for your application. Styles use a collection of Setters to apply style changes. The most commonly used Setter type is the property setter, which allows you to set a property. Event setters allow you to hook up event handlers as part of an applied style.

  • Styles can be set inline, but more frequently, they are defined in a Resources collection and are set by referring to the resource. You can apply a style to all instances of a control by setting the TargetType property to the appropriate type.

  • Styles are most commonly applied declaratively, but they can be applied in code by creating a new style dynamically or obtaining a reference to a preexisting Style resource.

  • You can create styles that inherit from other styles by using the BasedOn property.

  • Property triggers monitor the value of a dependency property and can apply Setters from their Setters collection when the monitored property equals a predetermined value. Multi-triggers monitor multiple properties and apply their Setters when all monitored properties match corresponding specified values. Data triggers and multi-data-triggers are analogous but monitor bound values instead of dependency properties.

  • Event triggers perform a set of Actions when a particular event is raised. They are used most commonly to control Animations.

  • Property values follow a strict order of precedence depending on how they are set.

Lesson Review

You can use the following questions to test your knowledge of the information in Lesson 1, “Styles.” The questions are also available on the companion CD if you prefer to review them in electronic form.

  1. Look at the following XAML snippet:

    <Window.Resources>
       <Style x:Key="Style1">
          <Setter Property="Label.Background" Value="Blue" />
          <Setter Property="Button.Foreground" Value="Red" />
          <Setter Property="Button.Background" Value="LimeGreen" />
       </Style>
    </Window.Resources>
    <Grid>
       <Button Height="23" Margin="81,0,122,58" Name="Button1"
          VerticalAlignment="Bottom">Button</Button>
    </Grid>

    Assuming that the developer hasn’t set any properties any other way, what is the Background color of Button1?

    1. Blue

    2. Red

    3. LimeGreen

    4. System Default

  2. Look at the following XAML snippet:

    <Window.Resources>
       <Style x:Key="Style1">
          <Style.Triggers>
             <MultiTrigger>
                <MultiTrigger.Conditions>
                   <Condition Property="TextBox.IsMouseOver"
                      Value="True" />
                   <Condition Property="TextBox.IsFocused"
                      Value="True" />
                </MultiTrigger.Conditions>
                <Setter Property="TextBox.Background"
                   Value="Red" />
             </MultiTrigger>
          </Style.Triggers>
       </Style>
    </Window.Resources>
    <Grid>
       <TextBox Style="{StaticResource Style1}" Height="21"
          Margin="75,0,83,108" Name="TextBox1"
          VerticalAlignment="Bottom" />
    </Grid>

    When will TextBox1 appear with a red background?

    1. When the mouse is over TextBox1

    2. When TextBox1 is focused

    3. When TextBox1 is focused and the mouse is over TextBox1

    4. All of the above

    5. Never

  3. Look at the following XAML snippet:

    <Window.Resources>
       <Style TargetType="Button">
          <Setter Property="Content" Value="Hello" />
          <Style.Triggers>
             <Trigger Property="IsMouseOver" Value="True">
                <Setter Property="Content" Value="World" />
             </Trigger>
             <Trigger Property="IsMouseOver" Value="False">
                <Setter Property="Content" Value="How are you?" />
             </Trigger>
          </Style.Triggers>
       </Style>
    </Window.Resources>
    <Grid>
       <Button Height="23" Margin="81,0,122,58" Name="Button1"
          VerticalAlignment="Bottom">Button</Button>
    </Grid>

    What does Button1 display when the mouse is NOT over the Button?

    1. Hello

    2. World

    3. Button

    4. How are you?