WinUI.TableView with C# Markup
You’re building a nice Uno Platform app with C# Markup, you drop in WinUI.TableView, wire up ItemsSource the usual way — and the app crashes.
InvalidOperationException: Setting this property directly is not allowed. Use TableView.ItemsSource instead.
Confusing, right? Your code looks correct. The binding syntax is fine. So what’s going on?
The Problem
C# Markup gives you a clean fluent API:
new TableView()
.ItemsSource(() => ViewModel.People);
Under the hood though, C# Markup resolves ItemsSource through ItemsControl — because that’s where the extension methods are defined. So it ends up setting:
ItemsControl.ItemsSourceProperty
That’s normally fine for standard controls. But WinUI.TableView hides the base property:
public new object? ItemsSource { get; set; }
We do this to support features like sorting and filtering, which require intercepting every assignment. Along with that, we register our own dependency property:
public new static DependencyProperty ItemsSourceProperty
So you end up with two completely separate DPs living side by side:
ItemsControl.ItemsSourcePropertyTableView.ItemsSourceProperty
C# Markup targets the first one. TableView guards it and throws. That’s your crash.
Why This Happens
Neither C# Markup nor TableView is doing something wrong here.
This is just property hiding in .NET — when you declare public new, you’re not overriding anything. You’re creating a brand new member that has no relationship to the base class version. C# Markup has no way of knowing that TableView wants a different DP, so it falls back to the one it knows about.
The Fix
Because TableView.ItemsSource is of type object, the built-in C# Markup extensions don’t apply. We need custom ones that target TableView.ItemsSourceProperty directly, covering all the common usage patterns:
public static class TableViewMarkupExtensions
{
// 1️⃣ Direct assignment
public static T ItemsSource<T>(this T element, object itemsSource) where T : TableView
{
element.ItemsSource = itemsSource;
return element;
}
// 2️⃣ Binding via DependencyPropertyBuilder
public static T ItemsSource<T>(this T element, Action<IDependencyPropertyBuilder<object>> configureProperty) where T : TableView
{
var instance = DependencyPropertyBuilder<object>.Instance;
configureProperty(instance);
instance.SetBinding(element, ItemsControl.ItemsSourceProperty, nameof(TableView.ItemsSource));
return element;
}
// 3️⃣ Strongly-typed binding
public static T ItemsSource<T, TSource>(this T element, Func<TSource> propertyBinding, [CallerArgumentExpression(nameof(propertyBinding))] string? propertyBindingExpression = null) where T : TableView
{
return ItemsSource(element, delegate (IDependencyPropertyBuilder<object> _)
{
_.Binding(propertyBinding, propertyBindingExpression);
});
}
// 4️⃣ Strongly-typed binding with conversion
public static T ItemsSource<T, TSource>(this T element, Func<TSource> propertyBinding, Func<TSource, object> convertDelegate, [CallerArgumentExpression("propertyBinding")] string? propertyBindingExpression = null) where T : TableView
{
return ItemsSource(element, delegate (IDependencyPropertyBuilder<object> _)
{
_.Binding(propertyBinding, propertyBindingExpression).Convert(convertDelegate);
});
}
}
Now It Works
With these extensions in place, both styles work exactly as you’d expect:
new TableView()
.ItemsSource(People);
new TableView()
.ItemsSource(() => vm.People);
The binding now correctly targets TableView.ItemsSourceProperty and the crash is gone.