.NET 多平台应用 UI (.NET MAUI) 数据绑定有两个主要问题:
绑定表达式不具有编译时验证。 相反,绑定在运行时解析。 因此,在应用程序未按预期运行或出现错误消息时,直到运行时才会检测到任何无效绑定。
它们不具有成本效益。 使用常规对象检查(反射)在运行时解析绑定,并且执行此操作的开销因平台而异。
编译的绑定通过在编译时而不是运行时解析绑定表达式来提高 .NET MAUI 应用程序中的数据绑定性能。 此外,绑定表达式的这种编译时验证可以提供更好的开发人员故障排除体验,因为无效绑定会被报告为生成错误。
使用已编译的绑定的过程:
确保已启用 XAML 编译。 有关 XAML 编译的详细信息,请参阅
XAML 编译
。
将
VisualElement
上的
x:DataType
属性设置为
VisualElement
及其子元素将绑定到的对象类型。
建议在设置
BindingContext
的视图层次结构中将
x:DataType
属性设置为相同级别。 但可以在视图层次结构中的任意位置重新定义此属性。
若要使用已编译的绑定,必须将
x:DataType
属性设置为字符串文字或使用
x:Type
标记扩展的类型。 在 XAML 编译时,会将任何无效绑定表达式报告为生成错误。 但是,XAML 编译器仅报告遇到的第一个无效绑定表达式的生成错误。 无论是在 XAML 还是代码中设置
BindingContext
,都将编译在
VisualElement
或其子元素上定义的任何有效绑定表达式。 编译绑定表达式会生成编译代码,该代码将从源上的属性获取值,并将在标记中指定的目标的属性上对其进行设置 。 此外,根据绑定表达式,生成的代码可能会观察到源属性值的更改并刷新目标属性,并可能会将更改从目标推送回源 。
对于定义 属性的任何绑定表达式,禁用已编译的
Source
绑定。 这是因为
Source
属性始终使用
x:Reference
标记扩展进行设置,该设置在编译时无法解析。
此外,多绑定目前不支持已编译的绑定。
使用已编译的绑定
以下示例演示如何在 .NET MAUI 视图和 viewmodel 属性之间使用已编译的绑定:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:DataBindingDemos"
x:Class="DataBindingDemos.CompiledColorSelectorPage"
x:DataType="local:HslColorViewModel"
Title="Compiled Color Selector">
<ContentPage.BindingContext>
<local:HslColorViewModel Color="Sienna" />
</ContentPage.BindingContext>
<StackLayout>
<BoxView Color="{Binding Color}"
... />
<StackLayout Margin="10, 0">
<Label Text="{Binding Name}" />
<Slider Value="{Binding Hue}" />
<Label Text="{Binding Hue, StringFormat='Hue = {0:F2}'}" />
<Slider Value="{Binding Saturation}" />
<Label Text="{Binding Saturation, StringFormat='Saturation = {0:F2}'}" />
<Slider Value="{Binding Luminosity}" />
<Label Text="{Binding Luminosity, StringFormat='Luminosity = {0:F2}'}" />
</StackLayout>
</StackLayout>
</ContentPage>
ContentPage实例化 并HslColorViewModel
初始化 Color
属性的属性元素标记BindingContext
中的 属性。 ContentPage还将 属性定义为 x:DataType
viewmodel 类型,指示将编译视图层次结构中的任何ContentPage绑定表达式。 通过更改任何绑定表达式以绑定到不存在的 viewmodel 属性,可对此进行验证,这将导致生成错误。 尽管此示例将 x:DataType
特性设置为字符串文字,但也可以将其设置为具有 x:Type
标记扩展的类型。 有关 x:Type
标记扩展的详细信息,请参阅 x:Type 标记扩展。
可以在视图层次结构中的任意点重新定义 x:DataType
属性。
BoxView、Label 元素和 Slider 视图从 ContentPage 继承绑定上下文。 这些视图都是引用 viewmodel 中的源属性的绑定目标。 对于 BoxView.Color
属性和 Label.Text
属性,数据绑定为 OneWay
- 视图中的属性根据 viewmodel 中的属性进行设置。 但是,Slider.Value
属性使用 TwoWay
绑定。 此模式允许从 viewmodel 设置每个 Slider,也允许从每个 Slider 设置 viewmodel。
首次运行该示例时,BoxViewLabel将根据实例化 viewmodel 时的初始Color
属性集,从 viewmodel 设置 、 元素和 Slider 元素。 操作滑块时, BoxView 和 Label 元素会相应地更新:
有关此颜色选择器的详细信息,请参阅 ViewModels 和属性更改通知。
在 DataTemplate 中使用已编译的绑定
DataTemplate
中的绑定在模板化的对象的上下文中进行解释。 因此,在 DataTemplate
中使用已编译的绑定时,DataTemplate
需要使用 x:DataType
属性来声明其数据对象的类型。
以下示例演示如何在 中使用 DataTemplate
编译的绑定:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:DataBindingDemos"
x:Class="DataBindingDemos.CompiledColorListPage"
Title="Compiled Color List">
<ListView x:Name="colorListView"
ItemsSource="{x:Static local:NamedColor.All}"
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:NamedColor">
<ViewCell>
<StackLayout Orientation="Horizontal">
<BoxView Color="{Binding Color}"
... />
<Label Text="{Binding FriendlyName}"
... />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<!-- The BoxView doesn't use compiled bindings -->
<BoxView Color="{Binding Source={x:Reference colorListView}, Path=SelectedItem.Color}"
... />
</Grid>
</ContentPage>
将 ListView.ItemsSource
属性设置为静态 NamedColor.All
属性。 类 NamedColor
使用 .NET 反射枚举类中的所有静态公共字段 Colors ,并将它们及其名称存储在可从静态 All
属性访问的集合中。 因此,ListView 由所有 NamedColor
实例填充。 对于 ListView 中的每个项,项的绑定上下文都被设置为 NamedColor
对象。 中的 BoxView 和 ViewCellLabel 元素被绑定到 NamedColor
属性。
将 DataTemplate
x:DataType
属性 NamedColor
定义为 类型,指示将编译视图层次结构中的任何 DataTemplate
绑定表达式。 通过更改任何绑定表达式来绑定到不存在的 NamedColor
属性,可对此进行验证,这将导致生成错误。 尽管此示例将 x:DataType
特性设置为字符串文字,但也可以将其设置为具有 x:Type
标记扩展的类型。 有关 x:Type
标记扩展的详细信息,请参阅 x:Type 标记扩展。
首次运行该示例时, ListView 将填充 NamedColor
实例。 选中 ListView 中的项时,BoxView.Color
属性被设置为 ListView 中所选项的颜色:
选择 ListView 中的其他项会更新 BoxView 的颜色。
将已编译的绑定与传统绑定相结合
绑定表达式仅针对定义了 x:DataType
属性的视图层次结构进行编译。 相反,未定义 x:DataType
属性的层次结构中的任何视图都将使用传统绑定。 因此,可以在页面上组合已编译的绑定和传统绑定。 例如,在之前的部分中,DataTemplate
中的视图使用已编译的绑定,而设置为 中所选颜色的 ListViewBoxView 则不使用。
因此仔细构造 x:DataType
属性可以生成使用已编译绑定和传统绑定的页面。 或者,可以使用 x:Null
标记扩展在视图层次结构中的任意点将 x:DataType
属性重新定义为 null
。 执行此操作表示视图层次结构中的任何绑定表达式都将使用传统绑定。 以下示例演示了此方法:
<StackLayout x:DataType="local:HslColorViewModel">
<StackLayout.BindingContext>
<local:HslColorViewModel Color="Sienna" />
</StackLayout.BindingContext>
<BoxView Color="{Binding Color}"
VerticalOptions="FillAndExpand" />
<StackLayout x:DataType="{x:Null}"
Margin="10, 0">
<Label Text="{Binding Name}" />
<Slider Value="{Binding Hue}" />
<Label Text="{Binding Hue, StringFormat='Hue = {0:F2}'}" />
<Slider Value="{Binding Saturation}" />
<Label Text="{Binding Saturation, StringFormat='Saturation = {0:F2}'}" />
<Slider Value="{Binding Luminosity}" />
<Label Text="{Binding Luminosity, StringFormat='Luminosity = {0:F2}'}" />
</StackLayout>
</StackLayout>
根 StackLayout 将 x:DataType
属性设置为 HslColorViewModel
类型,表示将编译根 StackLayout 视图结构中的任意绑定表达式。 但是,内部 StackLayout 使用 x:Null
标记表达式将 x:DataType
属性重新定义为 null
。 因此,内部 StackLayout 中的绑定表达式使用传统绑定。 只有根 StackLayout 视图结构层次中的 BoxView 使用已编译的绑定。
有关 x:Null
标记表达式的详细信息,请参阅 x:Null 标记扩展。
编译的绑定可提高数据绑定性能,其性能优势各不相同:
使用属性更改通知(即 OneWay
、OneWayToSource
或 TwoWay
绑定)的已编译绑定的解析速度比传统绑定快约 8 倍。
不使用属性更改通知(例如 OneTime
绑定)的已编译绑定的解析速度比传统绑定快约 20 倍。
BindingContext
在使用属性更改通知 ((即 OneWay
、 OneWayToSource
或 TwoWay
绑定) )的已编译绑定上设置 ,大约比在经典绑定上设置 BindingContext
快 5 倍。
在不使用属性更改通知(即 OneTime
绑定)的已编译绑定上设置 BindingContext
比在经典绑定上设置 BindingContext
快大约 7 倍。
这些性能差异在移动设备上更为明显,具体取决于所使用的平台、正在使用的操作系统的版本以及运行应用程序的设备。