{"id":112668,"date":"2026-09-02T07:00:00","date_gmt":"2026-09-02T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=112668"},"modified":"2026-09-02T21:45:32","modified_gmt":"2026-09-03T04:45:32","slug":"the-perils-of-binding-to-value-types-in-xaml","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20260902-00\/?p=112668","title":{"rendered":"The perils of binding to value types in XAML"},"content":{"rendered":"<p>A colleague ran into trouble with their XAML program. They were using a <code>FlipView<\/code> control to bind to a collection, but when the user tried to navigate the <code>FlipView<\/code> using an assistive technology tool, there were cases where the navigation failed.<\/p>\n<p>Some time later, they came back with the solution to the mystery.<\/p>\n<p>The team noticed that their data model consisted only of strings and other value types, so they decided to declare their data model as a <code>struct<\/code> rather than a full <code>runtimeclass<\/code>, thereby avoiding a lot of boilerplate typing.<\/p>\n<p>If defined as a <code>runtimeclass<\/code>:<\/p>\n<pre>\/\/ MyComponent.idl\r\nruntimeclass MyPageContent\r\n{\r\n    String Title { get; };\r\n    String Description { get; };\r\n    String LinkUri { get; };\r\n    Boolean IsNew{ get; };\r\n}\r\n\r\n\/\/ MyPageContent.h\r\n\r\nnamespace winrt::MyComponent\r\n{\r\n    struct MyPageContent : implements&lt;MyPageContent&gt;\r\n    {\r\n        MyPageContent(hstring const&amp; title,\r\n                    hstring const&amp; description,\r\n                    hstring const&amp; link,\r\n                    bool isNew) :\r\n            m_title(title),\r\n            m_description(description),\r\n            m_link(link),\r\n            m_isNew(isNew) {}\r\n\r\n        hstring Title() const { return m_title; }\r\n        hstring Description() const { return m_description; }\r\n        hstring Link() const { return m_link; }\r\n        bool IsNew() const { return m_isNew; }\r\n\r\n    private:\r\n        hstring m_title;\r\n        hstring m_description;\r\n        Windows::Foundation::Uri m_link;\r\n        bool m_isNew;\r\n    };\r\n}\r\n\r\n\/\/ Consumer.cpp\r\n\r\nm_pages.Append(winrt::make&lt;MyPageContent&gt;(\r\n                    title, description, link, isNew));\r\n<\/pre>\n<p>But if you define it as a <code>struct<\/code>, then most of this code isn&#8217;t necessary:<\/p>\n<pre>\/\/ MyComponent.idl\r\n<span style=\"border: solid 1px currentcolor;\">struct<\/span> MyPageContent\r\n{\r\n    String Title;\r\n    String Description;\r\n    String Link;\r\n    Boolean IsNew;\r\n}\r\n\r\n\/\/ <span style=\"text-decoration: line-through;\">MyPageContent.h<\/span> not needed\r\n\r\n\/\/ Consumer.cpp\r\n\r\nm_pages.Append(MyPageContent(title, description, link, isNew));\r\n<\/pre>\n<p><a href=\"https:\/\/en.wikipedia.org\/wiki\/Miller_Lite#Advertising\">Tastes great, less filling<\/a>.<\/p>\n<p>Now, the thing that makes value types value types is that they are copy-by-value, not copy-by-reference. This means that when XAML calls <code>GetAt(n)<\/code> on the <code>m_pages<\/code> to get the <var>n<\/var>th item, it gets a <i>copy<\/i> of the <code>MyPageContent<\/code> and binds to the copy.<\/p>\n<p>And that&#8217;s the source of the problem.<\/p>\n<p>When the code wants to navigate to a specific item at the request of the assistive technology tool, it passes the <code>MyPageContent<\/code> to navigate to, but that&#8217;s just another copy because value types are always passed by copy. XAML says, &#8220;I don&#8217;t have that guy&#8221; and fails the navigation. (XAML doesn&#8217;t realize that it has a guy who <i>looks just like<\/i> that guy. Not that it matters, because it&#8217;s not the same guy.)<\/p>\n<p>The clever shortcut turned out to be the problem.<\/p>\n<p>Now, while it&#8217;s true that there&#8217;s a bunch of typing needed to implement a C++\/WinRT runtime class, there are helpers to reduce the amount of typing required. In the Windows Implementation Library (wil), the <code>cppwinrt_authoring.h<\/code> header contains classes to simplify the implementation of events and properties. It exploits CRTP <a title=\"Exploiting C++\/WinRT CRTP: Property and event declarations\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230317-00\/?p=107946\"> in the same way I discussed some time ago<\/a>.<\/p>\n<pre>\/\/ MyPageContent.h\r\n\r\nnamespace winrt::MyComponent\r\n{\r\n    struct MyPageContent : implements&lt;MyPageContent&gt;\r\n    {\r\n        MyPageContent(hstring const&amp; title,\r\n                    hstring const&amp; description,\r\n                    hstring const&amp; link,\r\n                    bool isNew) :\r\n            m_title(title),\r\n            m_description(description),\r\n            m_link(link),\r\n            m_isNew(isNew) {}\r\n\r\n        <span style=\"border: solid 1px currentcolor; border-bottom: none;\">wil::single_threaded_property&lt;hstring&gt; Title;      <\/span>\r\n        <span style=\"border: 1px currentcolor; border-style: none solid;\">wil::single_threaded_property&lt;hstring&gt; Description;<\/span>\r\n        <span style=\"border: 1px currentcolor; border-style: none solid;\">wil::single_threaded_property&lt;hstring&gt; Link;       <\/span>\r\n        <span style=\"border: solid 1px currentcolor; border-top: none;\">wil::single_threaded_property&lt;bool&gt; IsNew;         <\/span>\r\n    };\r\n}\r\n<\/pre>\n<p>We can get away with using a <code>single_<wbr \/>threaded_<wbr \/>property<\/code> because the properties are written only at construction, so concurrent reads are not going to cause problems.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Loss of identity.<\/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-112668","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Loss of identity.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112668","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=112668"}],"version-history":[{"count":1,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112668\/revisions"}],"predecessor-version":[{"id":112669,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112668\/revisions\/112669"}],"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=112668"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=112668"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=112668"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}