How to define a `DependencyProperty` for a collection type in UWP?

Viewed 1291

I'm trying to create a DependencyObject which is created from Xaml. It has a DependencyProperty of type List<object> defined like so:

    public List<object> Map
    {
        get { return (List<object>)GetValue(MapProperty); }
        set { SetValue(MapProperty, value); }
    }

    // Using a DependencyProperty as the backing store for Map.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty MapProperty =
        DependencyProperty.Register("Map", typeof(List<object>), typeof(MyConverter), new PropertyMetadata(null));

Xaml:

        <MyConverter x:Key="myConverter">
            <MyConverter.Map>
                <TextPair First="App.Blank" Second ="App.BlankViewModel"/>
            </MyConverter.Map>
        </MyConverter>

I keep receiving Cannot add instance of type 'UwpApp.Xaml.TextPair' to a collection of type 'System.Collections.Generic.List<Object>. What can cause this error? Thank you.

2 Answers

Using the method shown by Fruchtzwerg you will get an unintentional singleton: one instance of List shared between all instances of MyCoverter.

See Collection-Type DependencyProperties in the Microsoft Docs.

To avoid this, you need to set the value separately, like this

public class MyConverter
{
    public IList Map
    {
        get { return (IList)GetValue(MapProperty); }
        set { SetValue(MapProperty, value); }
    }

    public static readonly DependencyProperty MapProperty =
        DependencyProperty.Register("Map", typeof(IList),
        typeof(MyConverter), new PropertyMetadata(null));

    public MyConverter 
    {
        // SetValue causes DependencyProperty precedence issue so bindings 
        // will not work. 
        // WPF supports SetCurrentValue() which solves this but UWP does not 
        this.SetValue(MapProperty, new List<object>());
    }
}

However, this will cause another problem due to DependencyProperty Value Precedence: you will no longer be able to bind to MapProperty as you have used SetValue which has a higher precedence.

So the best & correct solution is to use a CreateDefaultValueCallback. You can do this as follows:

public static readonly DependencyProperty MapProperty =
    DependencyProperty.Register("Map", typeof(IList),
    typeof(MyConverter), PropertyMetadata.Create(
    new CreateDefaultValueCallback(() =>
    {
        return new List<object>();
    }
}));
Related