Archive for March, 2008

Dependency Properties – Value Precendence

Thursday, March 13th, 2008

The value of a Dependency Properties in WPF can be set from many sources: Templates, Styles, Data Binding, Animation, Local, Inherited from Element Tree,… That’s the reason why they are called "Dependency" Properties. Their values depend on many sources.

To get not a total chaos, there’s a precedence list in WPF that rules which value finally will be set. Here’s the list beginning with the source with lowest precedence (1.) up to the Source with highest precedence (10.):

  1. default-value
  2. Inherited Value
  3. Theme Style Setter
  4. Theme Style Trigger
  5. Style Setter
  6. Template Trigger
  7. Style Trigger
  8. Local Value
  9. Animated Value
  10. Coerced Value

WPF or better say its Property Engine calculates the value of a Dependency Property out of these sources. Finally the ValidateValueCallback checks if the value is valid (If a ValidateValueCallback was registered with the Dependency Property). If the value is not valid, the calculated-value wouldn’t be used (of course).

You have always to be aware of this precedence list, even in simple scenarios. Let’s take a short example. The Attached Property TextElement.Foreground has the Inherits-Flag set to true, so the value of this property is inherited through the element tree and you can set it on any element by using the attached property syntax, e.g. on a StackPanel like below:

<StackPanel TextElement.Foreground="Green">
    <TextBox>Should be green</TextBox>
    <TextBlock>Should also be green</TextBlock>
</StackPanel>

What do you think is the result of the codesnippet above? Do both, TextBox and TextBlock, display green text? I wouldn’t ask if they would do so. :-)

No, not both Elements display green text. The text inside the TextBox is still in the standard black or darkGray. The TextBlock displays its text in green.

Mhm… why doesn’t the TextBox inherit the Foreground-Value from the StackPanel? If you set the TextElement.Foreground-Property locally, it works, and the text inside the TextBox gets green:

<TextBox TextElement.Foreground="Green">
Should be green </TextBox>

So lets find out why the TextBox doesn’t get the inherited Value and displays its text still in standard color. Let’s take a look at the TextBox’s ControlTemplate defined in the Theme-Style for Vista’s Aero-Theme (the theme my PC is running on). In the Triggers-Section of the ControlTemplate a Trigger is defined that is actived when the IsEnabled-Property of the TextBox is true. You can see the Templates Trigger-Section below. And as you can see there, the Trigger sets the TextElement.Foreground-Property to the Brush stored in the Field SystemColors.GrayTextBrush (by using the GrayTextBrushKey-ResourceKey).

<ControlTemplate.Triggers>
  <Trigger Property="UIElement.IsEnabled">
    <Setter Property="Panel.Background" TargetName="Bd">
      <Setter.Value>
        <DynamicResource 
     ResourceKey="{x:Static SystemColors.ControlBrushKey}"/>
      </Setter.Value>
    </Setter>
    <Setter Property="TextElement.Foreground">
      <Setter.Value>
        <DynamicResource 
    ResourceKey="{x:Static SystemColors.GrayTextBrushKey}"/>
      </Setter.Value>
    </Setter>
    <Trigger.Value>
      <s:Boolean>False</s:Boolean>
    </Trigger.Value>
  </Trigger>
</ControlTemplate.Triggers>

If you look to the precedence-list at the beginning of this post, you see that the Inherited Value is on position 2., and the Template Trigger is on Position 6. So the Template Trigger has higher precedence. In the StackPanel with a TextBox and a TextBlock, the Template Trigger of the TextBox will set the foreground, and not the inherited value. When we set the local value, this local value is used, cause local value is on a higher position than the Template Trigger in the precedence list.

To go one step further, let’s override the Template of the TextBox and don’t set the TextElement.Foreground-Property in the Trigger-Section. As a result of the following code with the new TextBox-Template you’ll see that both, TextBox and TextBlock will display green text. :-)

<Window.Resources>
  <Style TargetType="TextBox">
  <Setter Property="Template">
   <Setter.Value>
    <ControlTemplate TargetType="TextBoxBase" ...>
     <ScrollViewer Name="PART_ContentHost" .../>
    <ControlTemplate.Triggers>
     <Trigger Property="UIElement.IsEnabled">
      <Setter Property="TextElement.Foreground">
       <Setter.Value>
        <DynamicResource
  ResourceKey="{x:Static SystemColors.GrayTextBrushKey}" />
       </Setter.Value>
      </Setter>
      <Trigger.Value>
       <s:Boolean>False</s:Boolean>
      </Trigger.Value>
     </Trigger>
    </ControlTemplate.Triggers>
   </ControlTemplate>
   </Setter.Value>
  </Setter>
 </Style>
</Window.Resources>
<StackPanel TextElement.Foreground="Green">
 <TextBox>Should be green</TextBox>
 <TextBlock>Should also be green</TextBlock>
</StackPanel>