SkillAgentSearch skills...

maui-data-binding

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.

Install / Use

npx skills add dotnet/skills --skill maui-data-binding

Installs into whichever agent you are using.

About this skill
📄

SKILL.md

Installable skill definition

Quality Score

87/100

Supported Platforms

Universal

Tags

Our assessment of maui-data-binding

maui-data-binding scores 87/100 on our quality scale, 853rd of 2,398 Development & Engineering skills we index (top 36%).

Its SKILL.md is 14 KB long, well organised into 23 sections with 19 code examples: a thorough specification that gives an agent plenty to work with.

With 5,471 GitHub stars, it is one of the more widely adopted skills in the catalogue.

Substance
30/30
Structure
20/20
Description
15/15
Adoption
16/20
Freshness
15/15

Maintenance, license and trust

  • The repository was last updated 2 days ago, so maui-data-binding is actively maintained.
  • It is released under the MIT license, a permissive license that allows use, modification and commercial use with attribution.
  • Its trust signals score 100/100, with no cautions. These come from repository metadata, not a code audit — read the skill file before letting an agent act on it.

maui-data-binding compared with similar skills

All 4 of these similar skills score higher than maui-data-binding; compare them before choosing.

SkillScoreStarsUpdatedFormat
maui-data-binding (this skill)by dotnet875.5k2d agoSKILL.md
ai-job-searchby MadsLorentzen10044.0k5d agoCLAUDE.md
claude-howtoby luongnv8910041.7ktodayCLAUDE.md
algorithmic-artby anthropics100177.9k4d agoSKILL.md
pptxby anthropics100177.9k4d agoSKILL.md

Frequently asked questions

How do I install maui-data-binding?
Run npx skills add dotnet/skills --skill maui-data-binding. The install tabs above show the steps for each supported agent.
Which AI agents does maui-data-binding work with?
It is written for Universal, as a SKILL.md file. Other agents that read the same format can often use it too.
Is maui-data-binding safe to use?
It is MIT-licensed and scores 100/100 on trust signals. Skills are instructions an agent will follow, so read the file before installing it and do not approve commands you do not understand.
Is maui-data-binding still maintained?
The repository was last updated 2 days ago, so maui-data-binding is actively maintained.

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
  • Property-change-triggered animations — use built-in .NET MAUI animation APIs

Inputs

  • A .NET MAUI project targeting .NET 8 or later
  • 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 each DataTemplate | 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 own x: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:DataType only where BindingContext is set:

  1. Page / View root — where you assign BindingContext.
  2. 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.

<!-- ✅ Correct: x:DataType at the page root -->
<ContentPage xmlns:vm="clr-namespace:MyApp.ViewModels"
             x:DataType="vm:MainViewModel">
    <StackLayout>
        <Label Text="{Binding Title}" />
        <Slider Value="{Binding Progress}" />
    </StackLayout>
</ContentPage>

<!-- ❌ Wrong: x:DataType scattered on children -->
<ContentPage x:DataType="vm:MainViewModel">
    <StackLayout>
        <Label Text="{Binding Title}" />
        <Slider x:DataType="x:Object" Value="{Binding Progress}" />
    </StackLayout>
</ContentPage>

DataTemplate always needs its own x:DataType

<CollectionView ItemsSource="{Binding People}">
    <CollectionView.ItemTemplate>
        <DataTemplate x:DataType="model:Person">
            <Label Text="{Binding FullName}" />
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>

Enforce binding warnings as errors

| Warning | Meaning | |---------|---------| | XC0022 | 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.

Add to the .csproj:

<!-- Compile bindings that use Source= as well; otherwise XC0025 fires on every
     Source= / RelativeSource binding. As of .NET 10/11 this is on by default
     only for AOT / full-trim builds. -->
<MauiEnableXamlCBindingWithSourceCompilation>true</MauiEnableXamlCBindingWithSourceCompilation>
<WarningsAsErrors>XC0022;XC0025</WarningsAsErrors>

If you promote XC0025 without enabling that switch, make sure the project has no Source= / RelativeSource bindings — otherwise they will be reported.


Binding Modes

Set Mode explicitly only when overriding the default. Most properties already have the correct default:

| Mode | Direction | Use case | |------|-----------|----------| | OneWay | Source → Target | Display-only (default for most properties) | | TwoWay | Source ↔ Target | Editable controls (Entry.Text, Switch.IsToggled) | | OneWayToSource | Target → Source | Read user input without pushing back to UI | | OneTime | Source → Target (once) | Static values; no change-tracking overhead |

<!-- ✅ Defaults — omit Mode -->
<Label Text="{Binding Score}" />
<Entry Text="{Binding UserName}" />
<Switch IsToggled="{Binding DarkMode}" />

<!-- ✅ Override only when needed -->
<Label Text="{Binding Title, Mode=OneTime}" />
<Entry Text="{Binding SearchQuery, Mode=OneWayToSource}" />

<!-- ❌ Redundant — adds noise -->
<Label Text="{Binding Score, Mode=OneWay}" />
<Entry Text="{Binding UserName, Mode=TwoWay}" />

BindingContext and Property Paths

Every BindableObject inherits BindingContext from its parent unless explicitly set. Property paths support dot notation and indexers:

<Label Text="{Binding Address.City}" />
<Label Text="{Binding Items[0].Name}" />

Set BindingContext in XAML:

<ContentPage xmlns:vm="clr-namespace:MyApp.ViewModels"
             x:DataType="vm:MainViewModel">
    <ContentPage.BindingContext>
        <vm:MainViewModel />
    </ContentPage.BindingContext>
</ContentPage>

Or in code-behind (preferred with DI):

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;
}

Declare in XAML resources and consume:

<ContentPage.Resources>
    <local:IntToBoolConverter x:Key="IntToBool" />
</ContentPage.Resources>

<Switch IsToggled="{Binding Count, Converter={StaticResource IntToBool}}" />

ConverterParameter is always passed as a string — parse inside Convert:

<Label Text="{Binding Score, Converter={StaticResource ThresholdConverter},
              ConverterParameter=50}" />

Multi-Binding

Combine multiple source values with IMultiValueConverter:

<Label>
    <Label.Text>
        <MultiBinding Converter="{StaticResource FullNameConverter}">
            <Binding Path="FirstName" />
            <Binding Path="LastName" />
        </MultiBinding>
    </Label.Text>
</Label>
public class FullNameConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType,
        object parameter, CultureInfo culture)
    {
        if (values.Length == 2 && values[0] is string first
            && values[1] is string last)
            return $"{first} {last}";
        return string.Empty;
    }

    public object[] ConvertBack(object value, Type[] targetTypes,
        object parameter, CultureInfo culture)
        => throw new NotSupportedException();
}

Relative Bindings

| Source | Syntax | Use case | |--------|--------|----------| | Self | {Binding Source={RelativeSource Self}, Path=WidthRequest} | Bind to own properties | | Ancestor | {Binding BindingContext.Title, Source={RelativeSource AncestorType={x:Type ContentPage}}} | Reach parent BindingContext | | TemplatedParent | {Binding Source={RelativeSource TemplatedParent}, Path=Padding} | Inside ControlTemplate |

<!-- Square box: Height = Width -->
<BoxView WidthRequest="100"
         HeightRequest="{Binding Source={RelativeSource Self}, Path=WidthRequest}" />

StringFormat

Use Binding.StringFormat for simple display formatting without a converter:

<Label Text="{Binding Price, StringFormat='Total: {0:C2}'}" />
<Label Text="{Binding DueDate, StringFormat='{0:MMM dd, yyyy}'}" />

Wrap the format string in single quotes when it contains commas or braces.


Binding Fallbacks

  • **FallbackV

Truncated for display — read the full file on GitHub.

Related Skills

View on GitHub
GitHub Stars5.5k
CategoryDevelopment
Updated2d ago
Forks418

Languages

C#

Trust signals

100/100

From repository metadata: license, adoption, age and documentation. Not a code audit — see the Safety scan above for what the skill file itself contains.

No cautions