# Maui Data Binding

> >-

- **Type:** Skill
- **Install:** `agentstack add skill-dotnet-skills-maui-data-binding`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [dotnet](https://agentstack.voostack.com/s/dotnet)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [dotnet](https://github.com/dotnet)
- **Source:** https://github.com/dotnet/skills/tree/main/plugins/dotnet-maui/skills/maui-data-binding

## Install

```sh
agentstack add skill-dotnet-skills-maui-data-binding
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# .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](https://learn.microsoft.com/dotnet/maui/user-interface/animation/basic)

## 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)

---

## 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.

```xml

    
        
        
    

    
        
        
    

```

### DataTemplate always needs its own x:DataType

```xml

    
        
            
        
    

```

### Enforce binding warnings as errors

| Warning | Meaning |
|---------|---------|
| **XC0022** | Binding path not found on the declared `x:DataType` |
| **XC0023** | Property is not bindable |
| **XC0024** | `x:DataType` type not found |
| **XC0025** | Binding used without `x:DataType` (non-compiled fallback) |

Add to the `.csproj`:

```xml
XC0022;XC0025
```

---

## 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 |

```xml

```

---

## BindingContext and Property Paths

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

```xml

```

Set `BindingContext` in XAML:

```xml

    
        
    

```

Or in code-behind (preferred with DI):

```csharp
public MainPage(MainViewModel vm)
{
    InitializeComponent();
    BindingContext = vm;
}
```

---

## INotifyPropertyChanged and ObservableObject

### Manual implementation

```csharp
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)

```csharp
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):

```csharp
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:

```xml

    

```

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

```xml

```

---

## Multi-Binding

Combine multiple source values with `IMultiValueConverter`:

```xml

    
        
            
            
        
    

```

```csharp
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 |

```xml

```

---

## StringFormat

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

```xml

```

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

---

## Binding Fallbacks

- **FallbackValue** — used when the binding path cannot be resolved or the
  converter throws.
- **TargetNullValue** — used when the bound value is `null`.

```xml

```

---

## .NET 9+ Code Bindings (AOT-safe)

Fully AOT-safe, no reflection:

```csharp
label.SetBinding(Label.TextProperty,
    static (PersonViewModel vm) => vm.FullName);

entry.SetBinding(Entry.TextProperty,
    static (PersonViewModel vm) => vm.Age,
    mode: BindingMode.TwoWay,
    converter: new IntToStringConverter());
```

---

## Threading

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:

```csharp
// ✅ 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 | Add `x:DataType` at page root and every `DataTemplate`; enable `XC0025` as error |
| Forgetting to set `BindingContext` | Set in XAML (``) or inject via constructor |
| Specifying redundant `Mode=OneWay` / `Mode=TwoWay` | Omit `Mode` when using the control's default |
| ViewModel does not implement `INotifyPropertyChanged` | Use `ObservableObject` from CommunityToolkit.Mvvm or implement manually |
| Mutating `ObservableCollection` off the UI thread | Wrap mutations in `MainThread.BeginInvokeOnMainThread` |
| Complex converter chains in hot paths | Pre-compute values in the ViewModel instead |
| Using `x:DataType="x:Object"` to escape compiled bindings | Restructure bindings; keep compile-time safety |
| Binding to non-public properties | Binding targets must be `public` properties (fields are ignored) |

---

## References

- [Data binding overview](https://learn.microsoft.com/dotnet/maui/fundamentals/data-binding/)
- [Compiled bindings](https://learn.microsoft.com/dotnet/maui/fundamentals/data-binding/compiled-bindings)
- [Value converters](https://learn.microsoft.com/dotnet/maui/fundamentals/data-binding/converters)
- [Relative bindings](https://learn.microsoft.com/dotnet/maui/fundamentals/data-binding/relative-bindings)
- [Multi-bindings](https://learn.microsoft.com/dotnet/maui/fundamentals/data-binding/multibindings)
- [CommunityToolkit.Mvvm](https://learn.microsoft.com/dotnet/communitytoolkit/mvvm/)

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [dotnet](https://github.com/dotnet)
- **Source:** [dotnet/skills](https://github.com/dotnet/skills)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-dotnet-skills-maui-data-binding
- Seller: https://agentstack.voostack.com/s/dotnet
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
