Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c#

xaml

uwp

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.

like image 576
Andriy Shevchenko Avatar asked Oct 20 '25 09:10

Andriy Shevchenko


2 Answers

You defined the type of your DependencyProperty with typeof(List<object>). This means the property requires exactly this type. Since List<object> is not a TextPair we need to be more generic. Instead of using a special generic list type just use IList as type and add new List<object>() as default value. This should solve your problem.

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(new List<object>()));

EDIT: Looks like UWP behaves a bit different than WPF. To run this code in UWP you need to use the generic IList<object> instead of IList as property type.

like image 58
Fruchtzwerg Avatar answered Oct 22 '25 00:10

Fruchtzwerg


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>();
    }
}));
like image 32
Dr. Andrew Burnett-Thompson Avatar answered Oct 21 '25 22:10

Dr. Andrew Burnett-Thompson