Question

I understand that interesting things happen to attached properties in datatemplates but this one is very strange.

I have a behavior with a dependency property on it, the property is type List<DataStatePair>

[System.Windows.Markup.ContentProperty("StateDefinitions")]
public class MultiDataStateBehavior: StateBehaviourBase
{
    public List<DataStatePair> StateDefinitions
    {
        get { return (List<DataStatePair>)GetValue(StateDefinitionsProperty); }
        set { SetValue(StateDefinitionsProperty, value); }
    }

    // Using a DependencyProperty as the backing store for StateDefinitions.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty StateDefinitionsProperty =
        DependencyProperty.Register("StateDefinitions", typeof(List<DataStatePair>), typeof(MultiDataStateBehavior), new PropertyMetadata(new List<DataStatePair>()));
}

As you can see, I've marked it as the content property. The XAML looks like this:

<DataTemplate>
<!-- VISUAL STATES  OMITTED FOR BREVITY-->
<Grid x:Name="grid" Background="Transparent" ContextMenu="{StaticResource ContextMenu_ToolMenu}">
                    <i:Interaction.Behaviors>
                        <ext:MultiDataStateBehavior Binding="{Binding Type}">
                                <ext:DataStatePair State="None" Value="{x:Null}"/>
                                <ext:DataStatePair State="Gauge" Value="{x:Static jcm:ToolType.Gauge}"/>
                                <ext:DataStatePair State="Repeater" Value="{x:Static jcm:ToolType.Gauge}"/>
                        </ext:MultiDataStateBehavior>
                    </i:Interaction.Behaviors>
</Grid>
</DataTemplate>

The problem? 3 DataStatePair instances are added for each use of the datatemplate. I use the template 32 times in my app and get 96 DataStatePair instances in total. Grizzly! I understand how this is possible. The Behavior is static for the datatemplate but the DataStatePair instances are not and a List can be added to.

If change the dependency property to an IEnumerable everything breaks - it will not compile. If I set the property explicitly with an x:Array in XAML, everything works as expected - I only ever get 3 states. XAML below;

<i:Interaction.Behaviors>
                        <ext:MultiDataStateBehavior Binding="{Binding Type}" UseTransitionsOnLoad="True">
                            <ext:MultiDataStateBehavior.StateDefinitions>
                                <x:Array Type="{x:Type ext:DataStatePair}">
                                    <ext:DataStatePair State="None" Value="{x:Null}"/>
                                    <ext:DataStatePair State="Gauge" Value="{x:Static jcm:ToolType.Gauge}"/>
                                    <ext:DataStatePair State="Repeater" Value="{x:Static jcm:ToolType.Gauge}"/>
                                </x:Array>
                            </ext:MultiDataStateBehavior.StateDefinitions>
                        </ext:MultiDataStateBehavior>
</i:Interaction.Behaviors>

Does anyone know why this is and what the most elegant solution is. I can imagine a Microsoft implementation would not make you use x:Array.

EDIT : The x:Array solution breaks the blend designer.

XamlParseException: Add value to collection of type 'System.Collections.Generic.IEnumerable(SSW.WPFExtensions.DataStatePair)' threw an exception.

EDIT : Removing the [System.Windows.Markup.ContentProperty("StateDefinitions")] attribute definition works great. I dont understand what is going on!

WPF, what are you doing. WPF, STAHP

Was it helpful?

Solution

Potential solution...and this is a doozy.

Microsoft provides guidance for creating dependency properties that are collections here.

This documentation specifies that you should never declare the default value of the property as an instance but initialise the collection in the owning class' constructor and set the dependency property. eg.

public Aquarium() : base()
{
    SetValue(AquariumContentsPropertyKey, new List<FrameworkElement>()); 
}

...[the] single list default value is shared for all instances of Aquarium. If you ran the following test code, which is intended to show how you would instantiate two separate Aquarium instances and add a single different Fish to each of them, you would see a surprising result:

As for my design time exceptions. Same problem expect with new DataStatePair[0] which is immutable, terrible error reporting.

Conclusion - Someone at Microsoft is having a good chuckle at my expense.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top