{"id":102964,"date":"2019-10-04T07:00:00","date_gmt":"2019-10-04T14:00:00","guid":{"rendered":"http:\/\/devblogs.microsoft.com\/oldnewthing\/?p=102964"},"modified":"2019-10-03T17:56:39","modified_gmt":"2019-10-04T00:56:39","slug":"20191004-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20191004-00\/?p=102964","title":{"rendered":"How do I define a UWP XAML dependency property that is a collection type?"},"content":{"rendered":"<p>In XAML, there are at least three ways to specify the contents of a collection-typed dependency property. For concreteness, I&#8217;m going to discuss UWP XAML, but the same principle apply to WPF XAML.<\/p>\n<p>Let&#8217;s say that we have a Doodad object with a Widgets property. The Widgets property is a collection of Widget objects.<\/p>\n<p>First, we have the implicit collection:<\/p>\n<pre>&lt;Doodad&gt;\r\n    &lt;Doodad.Widgets&gt;\r\n        &lt;Widget ...\/&gt;\r\n        &lt;Widget ...\/&gt;\r\n        &lt;Widget ...\/&gt;\r\n    &lt;\/Doodad.Widgets&gt;\r\n&lt;\/Doodad&gt;\r\n<\/pre>\n<p>Second, we have the explicit collection:<\/p>\n<pre>&lt;Doodad&gt;\r\n    &lt;Doodad.Widgets&gt;\r\n        &lt;MyWidgetCollection&gt;\r\n            &lt;Widget ...\/&gt;\r\n            &lt;Widget ...\/&gt;\r\n            &lt;Widget ...\/&gt;\r\n        &lt;\/MyWidgetCollection&gt;\r\n    &lt;\/Doodad.Widgets&gt;\r\n&lt;\/Doodad&gt;\r\n<\/pre>\n<p>Third, we have binding:<\/p>\n<pre>&lt;Doodad Widgets=\"{Binding MyWidgets}\" ...\/&gt;\r\n&lt;Doodad Widgets=\"{x:Bind MyWidgets}\" ...\/&gt;\r\n<\/pre>\n<p>Okay, let&#8217;s tackle these in order.<\/p>\n<p>The XAML compiler converts the implicit collection into C# code that is roughly equivalent to the below:<\/p>\n<pre>var e1 = new Doodad();\r\nvar widgets = e1.Widgets;\r\nwidgets.Add(new Widget(...));\r\nwidgets.Add(new Widget(...));\r\nwidgets.Add(new Widget(...));\r\n<\/pre>\n<p>(You can ask the XAML compiler to generate C++, but I&#8217;ll use C# for notational convenience.)<\/p>\n<p>In order for the implicit collection to work, the property must have an initial value that is an empty collection.<\/p>\n<p>The explicit collection compiles to something like this:<\/p>\n<pre>var e1 = new Doodad();\r\nvar widgets = new MyWidgetCollection();\r\nwidgets.Add(new Widget(...));\r\nwidgets.Add(new Widget(...));\r\nwidgets.Add(new Widget(...));\r\ne1.Widgets = widgets;\r\n<\/pre>\n<p>In order for explicit collections to work, the property must be settable.<\/p>\n<p>Binding operates like this:<\/p>\n<pre>var e1 = FindTheDoodad();\r\ne1.Widgets = this.MyWidgets;\r\n<\/pre>\n<p>This is equivalent to the explicit collection, so that same solution for explicit collections works for binding, too.<\/p>\n<p>Okay, so how do we set up the dependency property so it satisfies all these requirements?<\/p>\n<p>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.<\/p>\n<pre>public class Doodad\r\n{\r\n  public static readonly DependencyProperty WidgetsProperty =\r\n    DependencyProperty.Register(\r\n      \"Widgets\",\r\n      typeof(IList&lt;Widget&gt;),\r\n      typeof(Doodad),\r\n      new PropertyMetadata(null));\r\n\r\n    public IList&lt;Widget&gt; Widgets\r\n    {\r\n      get =&gt; (IList&lt;Widget&gt;)GetValue(WidgetsProperty);\r\n      set =&gt; SetValue(WidgetsProperty, value);\r\n    }\r\n\r\n    public Doodad()\r\n    {\r\n        this.InitializeComponent();\r\n        Widgets = new List&lt;Widgets&gt;();\r\n    }\r\n  ..\r\n}\r\n<\/pre>\n<p>Note that the type of the property is <code>IList&lt;Widget&gt;<\/code> instead of <code>List&lt;Widget&gt;<\/code>. That way, clients can assign or bind a custom collection.<\/p>\n<p><b>Bonus chatter<\/b>: There is documentation on <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/framework\/wpf\/advanced\/collection-type-dependency-properties\"> how to create a WPF XAML dependency property that is a collection type<\/a>, but it makes the mistake of presenting <i>incorrect<\/i> 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&#8217;t work. (This is why, in my code samples, I&#8217;m careful to note that code in italics is wrong.)<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Putting previous guidance into practice.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-102964","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Putting previous guidance into practice.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/102964","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=102964"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/102964\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=102964"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=102964"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=102964"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}