October 4th, 2019

How do I define a UWP XAML dependency property that is a collection type?

In XAML, there are at least three ways to specify the contents of a collection-typed dependency property. For concreteness, I’m going to discuss UWP XAML, but the same principle apply to WPF XAML.

Let’s say that we have a Doodad object with a Widgets property. The Widgets property is a collection of Widget objects.

First, we have the implicit collection:

<Doodad>
    <Doodad.Widgets>
        <Widget .../>
        <Widget .../>
        <Widget .../>
    </Doodad.Widgets>
</Doodad>

Second, we have the explicit collection:

<Doodad>
    <Doodad.Widgets>
        <MyWidgetCollection>
            <Widget .../>
            <Widget .../>
            <Widget .../>
        </MyWidgetCollection>
    </Doodad.Widgets>
</Doodad>

Third, we have binding:

<Doodad Widgets="{Binding MyWidgets}" .../>
<Doodad Widgets="{x:Bind MyWidgets}" .../>

Okay, let’s tackle these in order.

The XAML compiler converts the implicit collection into C# code that is roughly equivalent to the below:

var e1 = new Doodad();
var widgets = e1.Widgets;
widgets.Add(new Widget(...));
widgets.Add(new Widget(...));
widgets.Add(new Widget(...));

(You can ask the XAML compiler to generate C++, but I’ll use C# for notational convenience.)

In order for the implicit collection to work, the property must have an initial value that is an empty collection.

The explicit collection compiles to something like this:

var e1 = new Doodad();
var widgets = new MyWidgetCollection();
widgets.Add(new Widget(...));
widgets.Add(new Widget(...));
widgets.Add(new Widget(...));
e1.Widgets = widgets;

In order for explicit collections to work, the property must be settable.

Binding operates like this:

var e1 = FindTheDoodad();
e1.Widgets = this.MyWidgets;

This is equivalent to the explicit collection, so that same solution for explicit collections works for binding, too.

Okay, so how do we set up the dependency property so it satisfies all these requirements?

Turns out this is a special case of what we looked at last time: The dependency property whose initial value is a mutable object. In this case, the mutable object is itself a collection.

public class Doodad
{
  public static readonly DependencyProperty WidgetsProperty =
    DependencyProperty.Register(
      "Widgets",
      typeof(IList<Widget>),
      typeof(Doodad),
      new PropertyMetadata(null));

    public IList<Widget> Widgets
    {
      get => (IList<Widget>)GetValue(WidgetsProperty);
      set => SetValue(WidgetsProperty, value);
    }

    public Doodad()
    {
        this.InitializeComponent();
        Widgets = new List<Widgets>();
    }
  ..
}

Note that the type of the property is IList<Widget> instead of List<Widget>. That way, clients can assign or bind a custom collection.

Bonus chatter: There is documentation on how to create a WPF XAML dependency property that is a collection type, but it makes the mistake of presenting incorrect code first, without any immediate indication that the code is incorrect. I copied the initial code block, since it looked complete, but the result didn’t work. (This is why, in my code samples, I’m careful to note that code in italics is wrong.)

Topics
Code

Author

Raymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information.

5 comments

Discussion is closed. Login to edit/delete existing comments.

  • Florian Schneidereit

    Since we’re into dependency properties, maybe Raymond can elaborate on why the UWP implementation is missing handy features like property value coercion. The workarounds I heard of are either complex, or break binding scenarios.

    • Joe Beans

      UWP is a crippled clone of Silverlight which was already a crippled version of WPF missing coercion and a bunch of other things. Even so, it was still insulting to see the Surface Duo running Android instead of UWP. Blaming app support is a hilarious reason to shelve Windows 10 for a joke OS from a lazy competitor whose products look like high school projects; with MS tooling you only need a team of maybe...

      Read more
      • Florian Schneidereit

        Yes, I know it is kind of a crippled Silverlight, but maybe Raymond can explain why they designed it like that, and not as powerful as WPF.

        App support is indeed a problem. Windows Mobile suffered and eventually died from it. One problem is that PWA never really took of, or still it takes too much time and the outcome is uncertain. If the Top-20-Apps from Android/iOS would all be ready as PWA - putting Windows...

        Read more
      • Joe Beans

        If MS had put the effort into mobile that they did with console gaming, they would have won. You only had to have a mustard seed of vision and you would have seen it. The tile/flat UI system looked better and was less work. But UWP was never given a complete set of desktop UI controls so there was never a bridge to adoption. Mobile and desktop were under digital apartheid literally isolated to separate...

        Read more