Freigeben über


property engine precedence (again)

A while back I posted something about how the property engine calculates its values.  Figured I would post an update, since we've continued to refine the property engine, and we've continued to refine the documentation...

Calculation is a multi-step process:
Base value -> expression evaluation -> animations -> coercion -> final answer

Base value precedence
From most to least important (not all stages apply to all properties):

1. Local value – eg, <Button FontSize=”12”/>

2. TemplatedParent’s template – An element has a TemplatedParent if it was created from a template (FrameworkTemplate).  So when you search the TemplatedParent’s template, you’re searching the template that created you.  Within the parent template:
• WPF first looks for any applicable triggers (if more than one applies, the last trigger defined wins)
• If there are no applicable triggers for this property, the “template local value” is used

        <ControlTemplate x:Key="foo" TargetType="{Type Button}">
          <Button Name="button" FontSize="12"/>  <!—template local value-->
          <ControlTemplate.Triggers>
            <Trigger Property="IsMouseOver" Value="true">
              <Setter Property="FontSize" Value="14" TargetName="button"/>
            </Trigger>
          </ControlTemplate.Triggers>
        </ControlTemplate>

3. Implicit Style (Style property only) – For the Style property, WPF will look through resources for a style matching this element’s type.

    <Grid>
      <Grid.Resources>
        <Style x:Key="{Type Button}">
          <Setter Property="FontSize" Value="14"/>
        </Style>
      </Grid.Resources>
  
      <Button/>
    </Grid>

Because there’s no local value or parent template value for Button.Style, WPF searches through resources looking for an appropriate style.

The type must match exactly – a <MyButton> subclass will not implicitly use a style for Button.

The resource walk is not the same as FindResource(), style lookup is a subset.  The implicit style lookup will never look to the theme dictionary.  If the element being implicitly styled is not a subclass of Control, the implicit style lookup will not look beyond the templated parent’s template.  (This is so users do not accidentally style controls they don’t even realize exist – such as a DockPanel buried deep inside the template for Button)

4. Style triggers – If multiple triggers apply, the last one wins.

5. Template triggers – If the element has a template, and that template has applicable triggers, it’s applied here.  If multiple triggers apply, the last one wins.

      <Button>
        <Button.Template>
          <ControlTemplate>
            <!-- insert template stuff here -->
            <ControlTemplate.Triggers>
              <Trigger Property="IsMouseOver" Value="true">
                <Setter Property="FontSize" Value="14"/>
              </Trigger>
            </ControlTemplate.Triggers>
          </Button.Template>
      </Button>

6. Style Setters – Any Setters in the style are applied here:

      <Button>
        <Button.Style>
          <Style x:Key="{Type Button}">
            <Setter Property="FontSize" Value="14"/>
          </Style>
        </Button.Style>
      </Button>

If there are multiple setters for the same property, last one wins.

7. DefaultStyle – Elements that support styling really have two styles, the Style property and the “default style”, which is found by searching theme resources for the value in the element’s DefaultStyleKey property.  (DefaultStyle is sometimes referred to as the “ThemeStyle”)  Within the style:
• WPF first looks for any applicable triggers (if more than one applies, the last trigger defined wins)
• If there are no applicable triggers for this property, the last <Setter> is used

8. Property inheritance – Look to your parent element and use its value.  (Unless your parent is using its default value, in which case this president’s stage will be considered to fail and the element will use its own default value, see next stage below)

WPF has more than one kind of parent, including logical tree parent and visual tree parent.  If the element has a logical tree parent, inheritance will use that, otherwise the visual tree parent is used.   There’s no multiple inheritance – if there is a logical parent but it doesn’t provide a value, the visual parent is not consulted.

The protected FrameworkElement.InheritanceBehavior property can be used to terminate the search prematurely by pretending the element has no parent.

9. Default value – the property’s PropertyMetadata.DefaultValue is used.  Like other property metadata, a specific element subclass can change its default value.  All properties are expected to have a DefaultValue of a valid type for that property.

Next -- expression evaluation, animations, and coercion