x:Bind does not work with C# record
Describe the bug I am try to x:bind a c# 9 record to the ItemsSource of an ItemsRepeater, with a corresponding DataTemplate. However, compilation fails with
XamlTypeInfo.g.cs(6465,13,6465,23): error CS8852: Init-only property or indexer 'BlogPost.Title' can only be assigned in an object initializer, or on 'this' or 'base' in an instance constructor or an 'init' accessor.
Steps to reproduce the bug
<ItemsRepeater ItemsSource="{x:Bind BlogPosts}">
<ItemsRepeater.ItemTemplate>
<DataTemplate x:DataType="local:BlogPost">
<TextBlock>{x:Bind Title}</TextBlock>
</DataTemplate>
</ItemsRepeater.ItemTemplate>
</ItemsRepeater>
public record BlogPost(string Title, string Teaser, Uri? Url, DateTime Published);
public partial class MyPage: Page, INotifyPropertyChanged
{
public ObservableCollection<BlogPost> BlogPosts { get; } = new()
{
new BlogPost("Test1", "Testing", null, DateTime.Now),
new BlogPost("Test2", "Testing2", null, DateTime.Now),
new BlogPost("Test2", "Testing2", null, DateTime.Now),
};
// shortened
}
Version Info
NuGet package version: WinUI 3 - Windows App SDK 0.8: 0.8.0
Windows app type:
| UWP | Win32 |
|---|---|
| Yes |
| Windows 10 version | Saw the problem? |
|---|---|
| Insider Build (xxxxx) | |
| 21H1 (19043) | Yes |
| October 2020 Update (19042) | |
| May 2020 Update (19041) | |
| November 2019 Update (18363) | |
| May 2019 Update (18362) | |
| October 2018 Update (17763) | |
| April 2018 Update (17134) | |
| Fall Creators Update (16299) | |
| Creators Update (15063) |
| Device form factor | Saw the problem? |
|---|---|
| Desktop | Yes |
| Xbox | |
| Surface Hub | |
| IoT |
Additional context The error message is referring to this generated method in XamlTypeInfo.g.cs which tries to set a property on a record:
private void set_295_BlogPost_Title(object instance, object Value)
{
var that = (global::BrandMatrix.BlogPost)instance;
that.Title = (global::System.String)Value;
}
Init Only Setters also seem to be broken in the same way:
public class BlogPost {
public string Title { get; init; }
//...
}
Of course private set; can be used as an alternative for now, but support for these features is needed to keep up with .net development. (Or at least a way better error message is needed)
I'm confused about why it's trying to call the setter in the first place? Have you set the defaultbindmode to TwoWay?
@JohnnyWestlake I have not not set DefaultBindMode. The error occurs when compiling, the setter appears to be generated even though it is not used.
@RealTommyKlein FYI
In WinAppSDK 1.0 with dotnet 6.0.1 the situation is still the same.
I've created a public readonly record struct in UWP and used the IsExternalInit polyfill method, and it works fine when binding. It seems odd that the generated code is trying to set the title for any reason.
@Marv51 Does the same thing occur if you setup the binding as <TextBlock Text="{x:Bind Title}"/> or make the struct readonly? (FYI you should always bind to the Text property directly to get proper graphical optimizations.)
@hawkerm I tested this again just now. <TextBlock Text="{x:Bind Title}"/> results in the exact same error message. Even removing the TextBlock and just keeping the x:DataType results in the wrong code getting generated:
XamlTypeInfo.g.cs:
private void set_9_BlogPost_Title(object instance, object Value)
{
var that = (global::App1.BlogPost)instance;
that.Title = (global::System.String)Value;
}
Unfortunately this isn't an easy fix on the WinUI side - the metadata loading/reflection the Xaml compiler is pretty dated and can't access custom modifiers for types, and the System.Runtime.CompilerServices.IsExternalInit custom modifier is used to specify init properties and records. Because of this limitation, from the Xaml compiler's POV, the type has a valid public setter so it automatically generates the setter code, even when the setter is actually invalid due to being on a record or init property . We may be able to add better logic for detecting if a property is actually used in markup to mitigate this, but the real fix would be switching the Xaml compiler over to a more modern way of loading metadata, which wouldn't happen for Windows App SDK 1.1.
@RealTommyKlein Thank you very much for explaining what is going on here and what the problems are.
@StephenLPeters @RealTommyKlein I can confirm this works fine in UWP, but with the same setup the XamlTypeInfo generator for WinUI 3 exhibits this issue w/ 1.0.3.
I just hit this building our new sample app in the Toolkit which we're cross-building for UWP and WinUI 3. The UWP head runs fine, but then the WinUI 3 compiler spits out the CS8852 error. I guess I don't understand why the XamlTypeInfo is even bothering to generate setters here at all if the property is only ever being read from in XAML (and not TwoWay bound)? Wouldn't that be the only case where the setter is required anyway?
Hi there, any update on this? I can of course stop using records and switch back to readonly structs, but that's a bit sad. Thanks!
Hi there, any update on this? I can of course stop using records and switch back to readonly structs, but that's a bit sad. Thanks!
Agreed.
Still not working. I'd really like to use record classes. But I can't when running in WindowsAppSdk...
I think this is still very important (re: #8638)
Here too. VERY important as providing a setter would make the messages which MUST be immutable mutable... . To have something "somehow" working I will write setters which will throw an Exception if called twice. But MS should take this issue more serious...
I hit the property initializer issue today as well. That's quite an oversight I'd also like to see improved.
Taking a quick look, I think the fix is to be made here:
https://github.com/microsoft/microsoft-ui-xaml/blob/aeed4c19e2fc7c2c093216a51b651f2f0890c930/src/src/XamlCompiler/BuildTasks/System.Xaml/System/Xaml/XamlMember.cs#L714-L722
See https://github.com/dotnet/runtime/issues/43088#issuecomment-704304906 for how to detect init-only using reflection.
This feels like an important scenario.
Also hit this issue today. Was looking to keep the data structure immutable.
This issue is getting old and it's been blocking basic use cases. Since this is 100% reproducible, I believe that it's not a complex fix. Please consider reassigning this to higher priority. 🙂
It is 2025 and the bug is still there...
Hi I was excited that you guys tackled some XAML compiler improvements in WinAppSDK 1.7. Maybe somebody could make sure that this issues is properly prioritized? @RealTommyKlein do you have any new insights maybe?
That comment from @Youssef1313 really tickles me here, MethodInfo seems to definitely contain the info needed. I don't really want to think about the fact that this over 4 years old issue might have a < 5 lines of code fix.
Almost 2026, just hit this.
In my case I added a HashSet<TRecord> of a record to my model in c# only, no xaml reference. Changing it to a IReadOnlyList<TRecord> actually stops it from generating the record sets so 🤷.