maui-data-binding
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chinese.NET MAUI Data Binding
.NET MAUI 数据绑定
Binding Modes
绑定模式
| Mode | Direction | Use case |
|---|---|---|
| Source → Target | Display-only (default for most properties) |
| Source ↔ Target | Editable controls ( |
| Target → Source | Read user input without pushing back to UI |
| Source → Target (once) | Static values; no change tracking overhead |
Set explicitly when the property default doesn't match your intent:
xml
<Entry Text="{Binding UserName, Mode=TwoWay}" />
<Label Text="{Binding Title, Mode=OneTime}" />| 模式 | 方向 | 适用场景 |
|---|---|---|
| 源 → 目标 | 仅显示(大多数属性的默认模式) |
| 源 ↔ 目标 | 可编辑控件( |
| 目标 → 源 | 读取用户输入但不回推至UI |
| 源 → 目标(仅一次) | 静态值;无变更追踪开销 |
当属性默认模式不符合需求时,需显式设置:
xml
<Entry Text="{Binding UserName, Mode=TwoWay}" />
<Label Text="{Binding Title, Mode=OneTime}" />BindingContext and Property Paths
BindingContext与属性路径
- Every inherits
BindableObjectfrom its parent unless explicitly set.BindingContext - Property paths support dot notation and indexers:
xml
<Label Text="{Binding Address.City}" />
<Label Text="{Binding Items[0].Name}" />- Set in XAML or code-behind:
BindingContext
xml
<ContentPage xmlns:vm="clr-namespace:MyApp.ViewModels"
x:DataType="vm:MainViewModel">
<ContentPage.BindingContext>
<vm:MainViewModel />
</ContentPage.BindingContext>
</ContentPage>- 所有会从父元素继承
BindableObject,除非显式设置。BindingContext - 属性路径支持点标记和索引器:
xml
<Label Text="{Binding Address.City}" />
<Label Text="{Binding Items[0].Name}" />- 可在XAML或代码后置中设置:
BindingContext
xml
<ContentPage xmlns:vm="clr-namespace:MyApp.ViewModels"
x:DataType="vm:MainViewModel">
<ContentPage.BindingContext>
<vm:MainViewModel />
</ContentPage.BindingContext>
</ContentPage>Compiled Bindings
编译绑定
Compiled bindings resolve at build time, delivering 8–20× faster binding resolution than reflection-based bindings.
编译绑定在构建时解析路径,比基于反射的绑定快8-20倍。
Enabling compiled bindings
启用编译绑定
Declare on the element or an ancestor:
x:DataTypexml
<ContentPage x:DataType="vm:MainViewModel">
<Label Text="{Binding UserName}" />
</ContentPage>在元素或其祖先上声明:
x:DataTypexml
<ContentPage x:DataType="vm:MainViewModel">
<Label Text="{Binding UserName}" />
</ContentPage>DataTemplate requires its own x:DataType
DataTemplate需单独声明x:DataType
DataTemplatexml
<CollectionView ItemsSource="{Binding People}">
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="model:Person">
<Label Text="{Binding FullName}" />
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>DataTemplatexml
<CollectionView ItemsSource="{Binding People}">
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="model:Person">
<Label Text="{Binding FullName}" />
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>Compiler warnings
编译器警告
| Warning | Meaning |
|---|---|
| XC0022 | Binding path not found on the declared |
| XC0023 | Property is not bindable |
| XC0024 | |
| XC0025 | Binding used without |
Treat these as errors in CI: .
<WarningsAsErrors>XC0022;XC0025</WarningsAsErrors>| 警告代码 | 含义 |
|---|---|
| XC0022 | 绑定路径在声明的 |
| XC0023 | 属性不可绑定 |
| XC0024 | |
| XC0025 | 使用绑定时未声明 |
在CI中将这些警告视为错误:。
<WarningsAsErrors>XC0022;XC0025</WarningsAsErrors>.NET 9+ compiled code bindings (SetBinding with lambda)
.NET 9+ 编译代码绑定(使用lambda的SetBinding)
csharp
// Fully AOT-safe, no reflection
label.SetBinding(Label.TextProperty,
static (PersonViewModel vm) => vm.FullName);
// With mode and converter
entry.SetBinding(Entry.TextProperty,
static (PersonViewModel vm) => vm.Age,
mode: BindingMode.TwoWay,
converter: new IntToStringConverter());csharp
// 完全支持NativeAOT,无反射
label.SetBinding(Label.TextProperty,
static (PersonViewModel vm) => vm.FullName);
// 带模式和转换器
entry.SetBinding(Entry.TextProperty,
static (PersonViewModel vm) => vm.Age,
mode: BindingMode.TwoWay,
converter: new IntToStringConverter());IValueConverter
IValueConverter
Implement with (source → target) and (target → source):
IValueConverterConvertConvertBackcsharp
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;
}实现接口,包含(源→目标)和(目标→源)方法:
IValueConverterConvertConvertBackcsharp
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;
}Declaring converters in XAML resources
在XAML资源中声明转换器
xml
<ContentPage.Resources>
<local:IntToBoolConverter x:Key="IntToBool" />
</ContentPage.Resources>
<Switch IsToggled="{Binding Count, Converter={StaticResource IntToBool}}" />xml
<ContentPage.Resources>
<local:IntToBoolConverter x:Key="IntToBool" />
</ContentPage.Resources>
<Switch IsToggled="{Binding Count, Converter={StaticResource IntToBool}}" />ConverterParameter
ConverterParameter
ConverterParameterConvertxml
<Label Text="{Binding Score, Converter={StaticResource ThresholdConverter},
ConverterParameter=50}" />csharp
int threshold = int.Parse((string)parameter);ConverterParameterConvertxml
<Label Text="{Binding Score, Converter={StaticResource ThresholdConverter},
ConverterParameter=50}" />csharp
int threshold = int.Parse((string)parameter);StringFormat
StringFormat
Use for simple display formatting without a converter:
Binding.StringFormatxml
<Label Text="{Binding Price, StringFormat='Total: {0:C2}'}" />
<Label Text="{Binding DueDate, StringFormat='{0:MMM dd, yyyy}'}" />Note: Wrap the format string in single quotes when it contains commas or braces.
对于简单的显示格式化,可使用替代转换器:
Binding.StringFormatxml
<Label Text="{Binding Price, StringFormat='Total: {0:C2}'}" />
<Label Text="{Binding DueDate, StringFormat='{0:MMM dd, yyyy}'}" />注意: 当格式字符串包含逗号或大括号时,需用单引号包裹。
Multi-Binding
多绑定
Combine multiple source values with :
IMultiValueConverterxml
<Label>
<Label.Text>
<MultiBinding Converter="{StaticResource FullNameConverter}"
StringFormat="{}{0}">
<Binding Path="FirstName" />
<Binding Path="LastName" />
</MultiBinding>
</Label.Text>
</Label>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();
}通过组合多个源值:
IMultiValueConverterxml
<Label>
<Label.Text>
<MultiBinding Converter="{StaticResource FullNameConverter}"
StringFormat="{}{0}">
<Binding Path="FirstName" />
<Binding Path="LastName" />
</MultiBinding>
</Label.Text>
</Label>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 | | Bind to own properties |
| Ancestor | | Reach parent BindingContext |
| TemplatedParent | | Inside ControlTemplate |
xml
<!-- Square box: Height = Width -->
<BoxView WidthRequest="100"
HeightRequest="{Binding Source={RelativeSource Self}, Path=WidthRequest}" />| 源对象 | 语法 | 适用场景 |
|---|---|---|
| 自身 | | 绑定至自身属性 |
| 祖先 | | 访问父级BindingContext |
| 模板父级 | | 在ControlTemplate内部使用 |
xml
<!-- 正方形框:高度 = 宽度 -->
<BoxView WidthRequest="100"
HeightRequest="{Binding Source={RelativeSource Self}, Path=WidthRequest}" />Binding Fallbacks
绑定回退值
- FallbackValue – used when the binding path cannot be resolved or the converter throws.
- TargetNullValue – used when the bound value is .
null
xml
<Label Text="{Binding MiddleName, TargetNullValue='(none)',
FallbackValue='unavailable'}" />
<Image Source="{Binding AvatarUrl, TargetNullValue='default_avatar.png'}" />- FallbackValue:当绑定路径无法解析或转换器抛出异常时使用。
- TargetNullValue:当绑定值为时使用。
null
xml
<Label Text="{Binding MiddleName, TargetNullValue='(none)',
FallbackValue='unavailable'}" />
<Image Source="{Binding AvatarUrl, TargetNullValue='default_avatar.png'}" />Threading
线程处理
MAUI automatically marshals property-change notifications to the UI thread. You can raise from any thread; the binding engine dispatches the update to the main thread.
PropertyChangedcsharp
// Safe from a background thread
await Task.Run(() =>
{
Items = LoadData(); // Raises PropertyChanged
OnPropertyChanged(nameof(Items));
});Caveat: Directmutations (Add/Remove) from background threads may still requireObservableCollection.MainThread.BeginInvokeOnMainThread
MAUI会自动将属性变更通知调度至UI线程。你可以在任意线程触发事件,绑定引擎会将更新调度至主线程。
PropertyChangedcsharp
// 在后台线程中执行安全
await Task.Run(() =>
{
Items = LoadData(); // 触发PropertyChanged
OnPropertyChanged(nameof(Items));
});注意: 从后台线程直接修改(如Add/Remove)可能仍需使用ObservableCollection。MainThread.BeginInvokeOnMainThread
Performance
性能优化
- Reflection overhead: Non-compiled bindings use reflection to resolve paths at runtime—measurably slower on large lists and startup.
- Compiled bindings eliminate reflection; always prefer them.
- NativeAOT / trimming: Reflection-based bindings may break under trimming. Compiled bindings (XAML or code
x:DataTypewith lambdas) are trimmer- and AOT-safe.SetBinding - Avoid complex converter chains in hot paths; pre-compute values in the ViewModel instead.
- Use mode for truly static data to skip change-tracking registration.
OneTime
- 反射开销:非编译绑定在运行时使用反射解析路径——在大型列表和启动阶段会明显变慢。
- 编译绑定:消除反射开销;始终优先使用。
- NativeAOT/裁剪:基于反射的绑定在裁剪环境下可能失效。编译绑定(XAML的或代码中使用lambda的
x:DataType)支持裁剪和AOT。SetBinding - 避免在热点路径中使用复杂转换器链;建议在ViewModel中预先计算值。
- 对于真正的静态数据,使用模式以跳过变更追踪注册。
OneTime