views:

60

answers:

1

I thought that element property syntax and attribute property syntax have no big semantical difference. However, I found that there must be some difference.

E.g. The following example just demonstrates a simple trigger:

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"&gt;
<Button><Button.Template>
  <ControlTemplate TargetType="{x:Type Button}">
    <TextBlock x:Name="hello" Text="Hello" />
    <ControlTemplate.Triggers>
      <Trigger Property="IsMouseOver" Value="True">
        <Setter Property="Foreground" Value="Red" TargetName="hello"/>
     </Trigger>
    </ControlTemplate.Triggers>
  </ControlTemplate>
</Button.Template></Button>
</Page>

However, if I used a element property syntax for property Property of trigger, it throws an exception saying that setter! (not trigger) requires both property and value.

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"&gt;
<Button><Button.Template>
  <ControlTemplate TargetType="{x:Type Button}">
    <TextBlock x:Name="hello" Text="Hello" />
    <ControlTemplate.Triggers>
      <Trigger Value="True">
        <Trigger.Property>IsMouseOver</Trigger.Property>
        <Setter Property="Foreground" Value="Red" TargetName="hello"/>
     </Trigger>
    </ControlTemplate.Triggers>
  </ControlTemplate>
</Button.Template></Button>
</Page>

So, what is the hidden difference between element property syntax and attribute property syntax?

+2  A: 

There shouldn't be a difference. I think you just found a bug in the XAML parser.

The framework has special handling for Setter, Trigger, and Condition. Check out Trigger.ReceiveTypeConverter with Reflector, which overrides the handling of property setters for the Value and Property properties. I think this is so that it can parse the Value property with a different type based on the Property property. For example, it parses "Red" as a Brush rather than just a String when it sees the Property is Foreground and that Foreground is of type Brush.

It looks like that hook overrides all sets of properties named Value or Property within a Trigger, but that it doesn't correctly handle the element property syntax. To see the effect, try creating a markup extension like this:

public class Test
    : MarkupExtension
{
    public DependencyProperty Property { get; set; }
    public DependencyProperty Property2 { get; set; }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return Property ?? Property2;
    }
}

The following XAML will get the same error as your second example, and you can verify by setting a breakpoint that Property is never set:

<Trigger.Property>
    <local:Test>
        <local:Test.Property>IsMouseOver</local:Test.Property>
    </local:Test>
</Trigger.Property>

However, this will work, because the property is not named "Property":

<Trigger.Property>
    <local:Test>
        <local:Test.Property2>IsMouseOver</local:Test.Property2>
    </local:Test>
</Trigger.Property>

And this will work, because it uses the attribute syntax:

<Trigger.Property>
    <local:Test Property="IsMouseOver"/>
</Trigger.Property>

If you really need to use the element property syntax, that will give you a workaround: create a MarkupExtension that has a Property of type DependencyProperty named something other than "Property", and return that in ProvideValue.

Quartermeister