Prioritizing value setting sources

As we have already seen, there are a number of ways of setting the values of Dependency Properties; we can set them directly in code, locally in XAML, or through the use of our CoerceValueCallback handlers for example. However, there are many more ways that they can be set. For example, they can also be set in styles, animations, or through property inheritance to name but a few.

When we data bind our View Model properties to Dependency Properties and find that the displayed value is not what we are expecting, one reason for this can be because another method of setting the property has a higher precedence and so, overrides our expected value. This is because all the methods of setting the values of Dependency Properties are ordered in terms of importance in a list called the Dependency Property Setting Precedence List. Let's take a look at that now:

  1. Property system coercion
  2. Animated properties
  3. Local value
  4. Template properties
  5. Implicit style (only applies to the Style property)
  6. Style triggers
  7. Template triggers
  8. Style setters
  9. Default (theme) style
  10. Inheritance
  11. Default value from Dependency Property metadata

Last on the list, with the lowest precedence at position eleven, are the default values that are specified in the Dependency Property declarations. Next up the list are changes caused by property inheritance. Remember that this can be defined in our Dependency Properties using the Inherits instance of the FrameworkPropertyMetadataOptions enumeration in the FrameworkPropertyMetadata input parameter of the DependencyProperty.Register method. Let's see an example of this to highlight this order of precedence:

<StackPanel TextElement.FontSize="20"> 
  <TextBlock Text="Black Text" /> 
  <StackPanel Orientation="Horizontal" TextElement.Foreground="Red"> 
    <TextBlock Text="Red Text" /> 
  </StackPanel> 
</StackPanel> 

In this first example, the TextBlock control in the outer StackPanel has its Foreground color set to black by the default value that was set in the data bound Text property. However, the TextBlock control inside the inner StackPanel has its default Foreground property value overridden by the TextElement.Foreground Attached Property value that is set on its parent control. It inherits the value of this property from the StackPanel and this demonstrates that properties set through property inheritance have a higher precedence than properties set with default values.

However, default property values that are set in theme styles follow on the precedence list, with the next lowest priority, and override property values set through inheritance. As it is quite difficult to come up with a short XAML example for this, we'll skip over this item and move onto the next. At number eight on the list, we have property values that have been set by style setters. Let's adjust our earlier example to demonstrate this:

<StackPanel TextElement.FontSize="20"> 
  <TextBlock Text="Black Text" /> 
  <StackPanel Orientation="Horizontal" TextElement.Foreground="Red"> 
    <TextBlock Text="Red Text" Margin="0,0,10,0" /> 
    <TextBlock Text="Green Text"> 
      <TextBlock.Style> 
        <Style TargetType="{x:Type TextBlock}"> 
          <Setter Property="Foreground" Value="Green" /> 
        </Style> 
      </TextBlock.Style> 
    </TextBlock> 
  </StackPanel> 
</StackPanel> 

In this example, the TextBlock control in the outer StackPanel still has its Foreground color set to black by the default value of the data bound Text property. The top TextBlock control inside the inner StackPanel still has its default Foreground property value overridden by the TextElement.Foreground value from its parent control. However, now we can also see that values that are set in a Style will override inherited property values. This is the output of this code snippet:

Next, at number seven on the precedence list, we have template triggers, which override property values that are set with style setters and all other previously mentioned methods of setting values. Note that this specifically deals with triggers that are declared within templates, such as the ControlTemplate, and does not relate to triggers that are declared within any Style.Triggers collections. Let's look at an example:

<Button Content="Blue Text" FontSize="20"> 
  <Button.Style> 
    <Style TargetType="{x:Type Button}"> 
      <Setter Property="Foreground" Value="Green" /> 
      <Setter Property="Control.Template"> 
        <Setter.Value> 
          <ControlTemplate TargetType="{x:Type Button}"> 
            <ContentPresenter /> 
            <ControlTemplate.Triggers>                   
              <Trigger Property="IsEnabled" Value="True"> 
                <Setter Property="Foreground" Value="Blue" /> 
              </Trigger> 
            </ControlTemplate.Triggers> 
          </ControlTemplate> 
        </Setter.Value> 
      </Setter> 
    </Style> 
  </Button.Style> 
</Button> 

In this example, we have declared a button and overridden its ControlTemplate, defining a new, minimal markup for it. In the style, we have set the Foreground property value to green in a setter. However, in our ControlTemplate, we have a Trigger that will override this value and set it to blue when its condition is met. Note that if we changed the trigger condition to false or removed the whole trigger, the button text would then become green, as set by the style.

Next up the list at position six are triggers that are declared within Style.Triggers collections. One important point to note here is that this only relates to styles that are either declared inline locally, in the current control's Resources section, or in the application resources file and not to default styles, which have a lower precedence value. We can extend our previous example by adding a new trigger into the Style.Triggers collection to highlight this new priority:

<Button Content="Orange Text" FontSize="20"> 
  <Button.Style> 
    <Style TargetType="{x:Type Button}"> 
      <Setter Property="Foreground" Value="Green" /> 
      <Setter Property="Control.Template"> 
        <Setter.Value> 
          <ControlTemplate TargetType="{x:Type Button}"> 
            <ContentPresenter /> 
            <ControlTemplate.Triggers> 
              <Trigger Property="IsEnabled" Value="True"> 
                <Setter Property="Foreground" Value="Blue" /> 
              </Trigger> 
            </ControlTemplate.Triggers> 
          </ControlTemplate> 
        </Setter.Value> 
      </Setter> 
      <Style.Triggers> 
        <Trigger Property="IsEnabled" Value="True"> 
          <Setter Property="Foreground" Value="Orange" /> 
        </Trigger> 
      </Style.Triggers> 
    </Style> 
  </Button.Style> 
</Button> 

When running this example, our text is now orange. The Foreground property value that is set by the trigger in the Triggers collection of the style has overridden the value set by the template trigger, which itself has overridden the value set by the style setter. Let's move on.

At number five on the list, we have implicit styles. Note that this special level of precedence only applies to the Style property and no others. A style can be implicitly set to all members of a type by specifying the target type and being declared without an x:Key directive set. Here is an example:

<Style TargetType="{x:Type Button}"> 
  <Setter Property="Foreground" Value="Green" /> 
</Style> 

The relevant style must either be declared in the current XAML page, or the Application.Resources section of the App.xaml file. Styles from themes are not included here, as they have a lower value precedence. Note that this special position in the list was only added in .NET 4 and is omitted from the .NET 3 documentation on the docs.microsoft.com website.

Next up the list at position four are properties that are set within either a ControlTemplate or a DataTemplate. If we set a property directly on any element within a template, that value will override all values set by methods with lower precedence. For example, if we directly set the Foreground property on the ContentPresenter from our previous example, then its value will override all other settings in that example and the button text will be red:

<ControlTemplate TargetType="{x:Type Button}"> 
  <ContentPresenter TextElement.Foreground="Red" /> 
  <ControlTemplate.Triggers> 
    <Trigger Property="IsEnabled" Value="True"> 
      <Setter Property="Foreground" Value="Blue" /> 
    </Trigger> 
  </ControlTemplate.Triggers> 
</ControlTemplate> 

At position three on the list, we have locally set values. To demonstrate this, we could just set the Foreground property on the actual button from the last full example, but instead let's highlight an extremely common mistake that a lot of developers make. Imagine a situation where we want to output a value predominantly in one color, but in another color under certain circumstances. Some developers might try something like this:

<TextBlock Text="{Binding Account.Amount, StringFormat={}{0:C}}"  
  Foreground="Green"> 
  <TextBlock.Style> 
    <Style TargetType="{x:Type TextBlock}"> 
      <Style.Triggers> 
        <DataTrigger Binding="{Binding Account.IsOverdrawn}" Value="True"> 
          <Setter Property="Foreground" Value="Red" /> 
        </DataTrigger> 
      </Style.Triggers> 
    </Style> 
  </TextBlock.Style> 
</TextBlock> 

Upon running this example, some might expect this to work and be stumped when it doesn't. The reason why this doesn't work is because local property settings have a higher value setting precedence than properties set by style triggers. The solution to correcting this mistake is to use our new found knowledge of this value setting precedence list and move the local property setting to a style setter, which has a lower precedence than the trigger:

<TextBlock Text="{Binding Account.Amount, StringFormat={}{0:C}}"> 
  <TextBlock.Style> 
    <Style TargetType="{x:Type TextBlock}"> 
      <Setter Property="Foreground" Value="Green" /> 
      <Style.Triggers> 
        <DataTrigger Binding="{Binding Account.IsOverdrawn}" Value="True"> 
          <Setter Property="Foreground" Value="Red" /> 
        </DataTrigger> 
      </Style.Triggers> 
    </Style> 
  </TextBlock.Style> 
</TextBlock> 

Now, the TextBlock.Foreground property will be set to green from the style setter and overridden by the trigger when the condition is true, as expected. Let's continue up the list to position two. In the penultimate position, we have property values that are set by animations. A very simple example can demonstrate this nicely for us:

<Rectangle Width="300" Height="300" Fill="Orange"> 
  <Rectangle.Triggers> 
    <EventTrigger RoutedEvent="Loaded"> 
      <BeginStoryboard> 
        <Storyboard Storyboard.TargetProperty="Width"> 
          <DoubleAnimation Duration="0:0:1" To="50" AutoReverse="True"
            RepeatBehavior="Forever" /> 
        </Storyboard> 
      </BeginStoryboard> 
    </EventTrigger> 
  </Rectangle.Triggers> 
</Rectangle> 

In this example, the animation overrides the locally set value of the Width property and the rectangle grows and shrinks as planned. If we think logically about this, then it is clear that the animation system had to feature at a very high position on the property setting precedence list. Otherwise, if it was much lower down the list, we wouldn't be able to animate anything.

However, properties that are set by animations are at number two of the list, which means that there is one place that a property can be set that will override even values set by animations. At number one on the list of Dependency Property Setting Precedence, with the absolutely highest priority setting, is the property coercion system that we discussed in the Dependency Properties section.

This could only really happen if we built a custom control that animated a custom Dependency Property that had particular requirements placed upon it, such as specifying that it should have a certain maximum or minimum value. In this case, we could enforce these rules in a CoerceValueCallback handler that is attached to the Dependency Property.

If we had these requirements that were enforced by the property coercion system, yet wanted to animate them in the UI, it again makes perfect sense that we would want our coerced values to override the values set by the animation. In this way, we could rest assured that our coerced property values will remain within the bounds that we set for them at all times.