|
| 1 | +# Native AOT compatibility |
| 2 | + |
| 3 | +`TableView` relies on runtime bindings and dynamic value resolution for features like sorting, filtering, editing, clipboard, and export. These mechanisms are not fully compatible with IL trimming and Native AOT by default. This page explains what you need to do to make your app work correctly under `<PublishAot>true</PublishAot>`. |
| 4 | + |
| 5 | +> **See also:** [Native AOT deployment overview (.NET)](https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/) · [C#/WinRT overview](https://learn.microsoft.com/en-us/windows/apps/develop/platform/csharp-winrt/) |
| 6 | +
|
| 7 | +## When this applies |
| 8 | + |
| 9 | +You need to follow this guidance if **any** of the following are true for your project: |
| 10 | + |
| 11 | +- `<PublishAot>true</PublishAot>` is set in your `.csproj` |
| 12 | +- `<PublishTrimmed>true</PublishTrimmed>` is set in your `.csproj` |
| 13 | +- You see AOT/trimming warnings related to `WinRT` or binding at compile time |
| 14 | + |
| 15 | +## The rule |
| 16 | + |
| 17 | +> Any model class whose **properties are accessed via traditional `{Binding}` expressions** must be marked with `[WinRT.GeneratedBindableCustomProperty]` and declared as `partial`. |
| 18 | +
|
| 19 | +Traditional binding (`{Binding PropertyName}`) resolves property values at runtime using the WinRT binding infrastructure. Under Native AOT, that infrastructure requires the `[WinRT.GeneratedBindableCustomProperty]` attribute to generate the necessary binding metadata at compile time. This attribute is part of [C#/WinRT](https://github.com/microsoft/CsWinRT). |
| 20 | + |
| 21 | +## What needs the attribute |
| 22 | + |
| 23 | +### Bound columns (attribute required) |
| 24 | + |
| 25 | +Columns that use a `Binding` property — all built-in bound column types — resolve values at runtime through the WinRT binding system: |
| 26 | + |
| 27 | +```xml |
| 28 | +<tv:TableViewTextColumn Header="Name" Binding="{Binding Name}" /> |
| 29 | +<tv:TableViewNumberColumn Header="Price" Binding="{Binding Price}" /> |
| 30 | +<tv:TableViewCheckBoxColumn Header="Active" Binding="{Binding IsActive}" /> |
| 31 | +<tv:TableViewDateColumn Header="Due" Binding="{Binding DueDate}" /> |
| 32 | +<tv:TableViewTimeColumn Header="Time" Binding="{Binding ActiveAt}" /> |
| 33 | +<tv:TableViewComboBoxColumn Header="Role" Binding="{Binding Role}" /> |
| 34 | +<tv:TableViewHyperlinkColumn Header="Url" Binding="{Binding Url}" /> |
| 35 | +``` |
| 36 | + |
| 37 | +Any model class whose properties are accessed by these columns **must** have the attribute. |
| 38 | + |
| 39 | +### Template columns with `{Binding}` (attribute required) |
| 40 | + |
| 41 | +If your `CellTemplate` or `EditingTemplate` uses `{Binding}` to access model properties, the attribute is still required: |
| 42 | + |
| 43 | +```xml |
| 44 | +<tv:TableViewTemplateColumn Header="Status"> |
| 45 | + <tv:TableViewTemplateColumn.CellTemplate> |
| 46 | + <DataTemplate> |
| 47 | + <!-- {Binding} requires the attribute on the model --> |
| 48 | + <TextBlock Text="{Binding StatusLabel}" /> |
| 49 | + </DataTemplate> |
| 50 | + </tv:TableViewTemplateColumn.CellTemplate> |
| 51 | +</tv:TableViewTemplateColumn> |
| 52 | +``` |
| 53 | + |
| 54 | +### Template columns with `{x:Bind}` (attribute NOT required) |
| 55 | + |
| 56 | +When a `TableViewTemplateColumn` uses `{x:Bind}` exclusively in its templates, the binding is compiled at build time and does **not** require the attribute on the model: |
| 57 | + |
| 58 | +```xml |
| 59 | +<tv:TableViewTemplateColumn Header="Status"> |
| 60 | + <tv:TableViewTemplateColumn.CellTemplate> |
| 61 | + <DataTemplate x:DataType="local:Product"> |
| 62 | + <!-- x:Bind generates compile-time code — no attribute needed --> |
| 63 | + <TextBlock Text="{x:Bind StatusLabel}" /> |
| 64 | + </DataTemplate> |
| 65 | + </tv:TableViewTemplateColumn.CellTemplate> |
| 66 | +</tv:TableViewTemplateColumn> |
| 67 | +``` |
| 68 | + |
| 69 | +## How to apply the attribute |
| 70 | + |
| 71 | +Add `[WinRT.GeneratedBindableCustomProperty]` to your model class and mark it as `partial`: |
| 72 | + |
| 73 | +```csharp |
| 74 | +using WinRT; |
| 75 | + |
| 76 | +[GeneratedBindableCustomProperty] |
| 77 | +public partial class Product |
| 78 | +{ |
| 79 | + public string? Name { get; set; } |
| 80 | + public double Price { get; set; } |
| 81 | + public bool InStock { get; set; } |
| 82 | +} |
| 83 | +``` |
| 84 | + |
| 85 | +> **Important:** The class **must** be `partial`. Without `partial`, the source generator cannot emit the required binding implementation and the build will fail. |
| 86 | +
|
| 87 | +## Using CommunityToolkit.Mvvm |
| 88 | + |
| 89 | +If your models use [`ObservableObject`](https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/observableobject) from [CommunityToolkit.Mvvm](https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/), combine both attributes. Use `partial` properties with [`[ObservableProperty]`](https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/generators/observableproperty) as normal: |
| 90 | + |
| 91 | +```csharp |
| 92 | +using CommunityToolkit.Mvvm.ComponentModel; |
| 93 | +using WinRT; |
| 94 | + |
| 95 | +[GeneratedBindableCustomProperty] |
| 96 | +public partial class Product : ObservableObject |
| 97 | +{ |
| 98 | + [ObservableProperty] |
| 99 | + public partial string? Name { get; set; } |
| 100 | + |
| 101 | + [ObservableProperty] |
| 102 | + public partial double Price { get; set; } |
| 103 | + |
| 104 | + [ObservableProperty] |
| 105 | + public partial bool InStock { get; set; } |
| 106 | +} |
| 107 | +``` |
| 108 | + |
| 109 | +## Nested models |
| 110 | + |
| 111 | +If a column binds to a nested property (e.g. `Binding="{Binding Address.City}"`), the **nested type** must also have the attribute: |
| 112 | + |
| 113 | +```csharp |
| 114 | +[GeneratedBindableCustomProperty] |
| 115 | +public partial class Order |
| 116 | +{ |
| 117 | + [ObservableProperty] |
| 118 | + public partial string? OrderNumber { get; set; } |
| 119 | + |
| 120 | + [ObservableProperty] |
| 121 | + public partial Address? ShippingAddress { get; set; } |
| 122 | +} |
| 123 | + |
| 124 | +[GeneratedBindableCustomProperty] // Required because columns bind into Address properties |
| 125 | +public partial class Address |
| 126 | +{ |
| 127 | + [ObservableProperty] |
| 128 | + public partial string? City { get; set; } |
| 129 | + |
| 130 | + [ObservableProperty] |
| 131 | + public partial string? Country { get; set; } |
| 132 | +} |
| 133 | +``` |
| 134 | + |
| 135 | +## Project configuration |
| 136 | + |
| 137 | +Enable AOT and set the CsWinRT warning level to catch any remaining issues at build time: |
| 138 | + |
| 139 | +```xml |
| 140 | +<PropertyGroup> |
| 141 | + <PublishAot>true</PublishAot> |
| 142 | + <AllowUnsafeBlocks>true</AllowUnsafeBlocks> |
| 143 | + <CsWinRTAotWarningLevel>2</CsWinRTAotWarningLevel> |
| 144 | +</PropertyGroup> |
| 145 | + |
| 146 | +<!-- Trim only for Release builds --> |
| 147 | +<PropertyGroup> |
| 148 | + <PublishTrimmed Condition="'$(Configuration)' != 'Debug'">true</PublishTrimmed> |
| 149 | +</PropertyGroup> |
| 150 | +``` |
| 151 | + |
| 152 | +`CsWinRTAotWarningLevel=2` upgrades [CsWinRT](https://github.com/microsoft/CsWinRT) AOT hints to errors so missing attributes are caught during the build rather than at runtime. |
| 153 | + |
| 154 | +## Notes and limitations |
| 155 | + |
| 156 | +- Only WinUI 3 (Windows) targets support Native AOT. Uno Platform targets have their own AOT characteristics — consult the [Uno Platform documentation](https://platform.uno/docs/articles/uno-development/aot-compilation.html) for those targets. |
| 157 | +- `AutoGenerateColumns="True"` uses reflection to discover properties. This works with the attribute in place, but for maximum AOT compatibility prefer explicit columns (`AutoGenerateColumns="False"`). |
| 158 | +- `SortMemberPath` uses reflection-based property access when set. Ensure the model has the attribute when using `SortMemberPath`. |
| 159 | + |
| 160 | +## Related articles |
| 161 | + |
| 162 | +- [Binding data](binding-data.md) |
| 163 | +- [Defining columns](defining-columns.md) |
| 164 | +- [Column types](column-types.md) |
| 165 | +- [Performance guidance](performance.md) |
| 166 | +- [.NET Native AOT deployment overview](https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/) |
| 167 | +- [C#/WinRT overview](https://learn.microsoft.com/en-us/windows/apps/develop/platform/csharp-winrt/) |
| 168 | +- [CommunityToolkit.Mvvm](https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/) |
| 169 | +- [CsWinRT on GitHub](https://github.com/microsoft/CsWinRT) |
0 commit comments