Download plugins/dotnet-maui/skills/maui-data-binding/SKILL.md from the publisher, or copy the path and open the repository. PlugMCP lists the skill and does not host the file.
Where does maui-data-binding live?
The publisher keeps it at https://github.com/dotnet/skills.
Which agents can use maui-data-binding?
Claude, Cursor, and Codex can read a SKILL.md you add from the publisher's repository.
>-
name: maui-data-binding
description: >-
Guidance for .NET MAUI XAML and C# data bindings — compiled bindings,
INotifyPropertyChanged / ObservableObject, value converters, binding modes,
multi-binding, relative bindings, fallbacks, and MVVM best practices.
USE FOR: setting up compiled bindings with x:DataType, implementing
INotifyPropertyChanged or CommunityToolkit ObservableObject, creating
IValueConverter / IMultiValueConverter, choosing binding modes, configuring
BindingContext, relative bindings, binding fallbacks, StringFormat,
code-behind SetBinding with lambdas, and enforcing XC0022/XC0025 warnings.
DO NOT USE FOR: CollectionView item templates and layouts (use
maui-collectionview), Shell navigation data passing (use
maui-shell-navigation), dependency injection (use maui-dependency-injection),
or animations triggered by property changes (use .NET MAUI animation APIs).
license: MIT
.NET MAUI Data Binding
Wire UI controls to ViewModel properties with compile-time safety, correct
change notification, and minimal overhead. Prefer compiled bindings everywhere
and treat binding warnings as build errors.
When to Use
Adding x:DataType compiled bindings to a new or existing page
Implementing INotifyPropertyChanged or CommunityToolkit ObservableObject
Creating or consuming IValueConverter / IMultiValueConverter
Choosing the correct BindingMode for a control property
Setting BindingContext in XAML or code-behind
Using relative bindings (Self, AncestorType, TemplatedParent)
Applying StringFormat, FallbackValue, or TargetNullValue
Writing AOT-safe code bindings with SetBinding and lambdas (.NET 9+)
When Not to Use
CollectionView layouts / templates — use the maui-collectionview skill
Shell navigation parameters — use the maui-shell-navigation skill
Service registration / DI — use the maui-dependency-injection skill
XAML pages or C# code-behind where bindings are declared
A ViewModel class (or plan to create one)
Rules That Change the Answer
Apply these to every binding answer — they are the differences between "it compiles"
and "it actually updates the UI".
Situation
Do this
Not this
Deciding where x:DataType goes
Put it wherever a binding scope starts — the page/view root, and eachDataTemplate
Scattering it on arbitrary children that share the parent's BindingContext
A binding falls back to reflection (XC0022 / XC0023)
Add the right x:DataType for that binding scope; for XC0023 remove the explicit x:DataType="{x:Null}"
x:DataType="x:Object" to silence it — this disables compile-time checking
A DataTemplate inherits x:DataType from an outer scope (XC0024)
Give the DataTemplate its ownx:DataType
Leaving it to resolve against the wrong type
ViewModel change notification
ObservableObject + [ObservableProperty], or implement INotifyPropertyChanged
A plain POCO base class — bindings will never update
Bindings show blank
Check BindingContext is actually set
Assuming the binding path is wrong
Enforcing compiled bindings
Set MauiEnableXamlCBindingWithSourceCompilation to true, then<WarningsAsErrors>XC0022;XC0025</WarningsAsErrors>
Promoting XC0025 without the switch if the project uses Source= / RelativeSource bindings
Do not restructure a ViewModel or add a converter that the user did not ask for
and that fixes no real defect. Adding x:DataType is different: when you are
already editing a page's bindings, recommending compiled bindings is in scope.
Compiled Bindings — x:DataType Placement
Compiled bindings are 8–20× faster than reflection-based bindings and are
required for NativeAOT / trimming. Enable them with x:DataType.
Placement rules
Set x:DataTypeonly where BindingContext is set:
Page / View root — where you assign BindingContext.
DataTemplate — which creates a new binding scope.
Do not scatter x:DataType on arbitrary child elements. Adding
x:DataType="x:Object" on children to escape compiled bindings is an
anti-pattern — it disables compile-time checking and reintroduces reflection.
Binding used without x:DataType in scope — not compiled, falls back to reflection
XC0023
Binding not compiled because x:DataType is explicitly null
XC0024
x:DataType came from an outer scope — annotate the DataTemplate with its own x:DataType
XC0025
Binding not compiled because it has an explicit Source — enable <MauiEnableXamlCBindingWithSourceCompilation>
These four codes are verified against .NET 10 / .NET 11 MAUI
(Build.Tasks/BuildException.cs, ErrorMessages.resx). Diagnostic numbering is
SDK-band-sensitive — re-check against BuildException.cs before relying on it on a
newer SDK.
public MainPage(MainViewModel vm)
{
InitializeComponent();
BindingContext = vm;
}
INotifyPropertyChanged and ObservableObject
Manual implementation
public class MainViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
private string _title = string.Empty;
public string Title
{
get => _title;
set
{
if (_title != value)
{
_title = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Title)));
}
}
}
}
CommunityToolkit.Mvvm (recommended)
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
public partial class MainViewModel : ObservableObject
{
[ObservableProperty]
private string _title = string.Empty;
[RelayCommand]
private async Task LoadDataAsync() { /* ... */ }
}
The source generator creates the Title property, PropertyChanged raise,
and LoadDataCommand automatically.
Value Converters — IValueConverter
Implement Convert (source → target) and ConvertBack (target → source):
public class IntToBoolConverter : IValueConverter
{
public object? Convert(object? value, Type targetType,
object? parameter, CultureInfo culture)
=> value is int i && i != 0;
public object? ConvertBack(object? value, Type targetType,
object? parameter, CultureInfo culture)
=> value is true ? 1 : 0;
}
MAUI automatically marshals PropertyChanged to the UI thread — you can raise
it from any thread. However, direct ObservableCollection mutations
(Add / Remove) from background threads may crash:
// ✅ Safe — PropertyChanged is auto-marshalled
await Task.Run(() => Title = "Loaded");
// ⚠️ ObservableCollection.Add — dispatch to UI thread
MainThread.BeginInvokeOnMainThread(() => Items.Add(newItem));
Common Pitfalls
Mistake
Fix
Missing x:DataType — bindings silently fall back to reflection
Download plugins/dotnet-maui/skills/maui-data-binding/SKILL.md from the publisher, or copy the path and open the repository. PlugMCP lists the skill and does not host the file.
Where does maui-data-binding live?
The publisher keeps it at https://github.com/dotnet/skills.
Which agents can use maui-data-binding?
Claude, Cursor, and Codex can read a SKILL.md you add from the publisher's repository.