在 XAML 中使用数据绑定

已完成

可以使用标记扩展在代码或 XAML 中声明数据绑定。 本单元讨论后者,因为这是创建绑定最常用的方法。 更喜欢使用 XAML 有几点原因。 第一,大多数人将绑定视为 UI 代码的一部分,因为绑定会获取 UI 要显示的数据。 第二,有一个名为 Binding 的标记扩展,易于操作。

什么是数据绑定

绑定将两个属性绑定在一起。 一个属性位于 UI 中,另一个属性位于数据模型对象中。 如果任一属性的值发生更改,绑定对象可以更新另一个属性。 换言之,绑定是同步 UI 和数据的中间对象。 我们使用术语“源”和“目标”来标识所涉及到的两个对象:

  • :源可以是任何类型的对象。 在实践中,通常会使用数据对象作为源。 你需要标识该源对象上的属性以参与绑定。 通过在绑定中设置 Path 属性来标识该属性。

  • 目标:目标是使用称为 BindableProperty 的特殊属性实现的属性。 具有 BindableProperty 的对象必须派生自 BindableObject。 .NET MAUI 中提供的所有控件都派生自 BindableObject,其大部分属性为 BindableProperties

下图说明了绑定如何成为两个属性之间的中间对象:

此关系显示了一个绑定,此绑定用作源对象属性与目标对象可绑定属性之间的媒介。

如何在 XAML 中创建数据绑定

我们来看看使用 {Binding} 标记扩展在 XAML 创建的简单示例绑定。 它将源的 WeatherService.Humidity 属性绑定到 UI 控件的 Text 属性。

<VerticalStackLayout Margin="10">
    <VerticalStackLayout.Resources>
        <ResourceDictionary>
            <services:WeatherService x:Key="myWeatherService" />
        </ResourceDictionary>
    </VerticalStackLayout.Resources>

    <Label Text="{Binding Path=Humidity, Source={StaticResource myWeatherService}}" />
</VerticalStackLayout>

绑定源为:

  • WeatherService 类型的对象实例。 系统通过 {StaticResource ...} XAML 扩展引用该实例,该扩展指向堆栈布局的资源字典中的对象。

  • Path 指向 WeatherService 类型上名为 Humidity 的属性。

    Path{Binding} 语法上的第一个未命名参数,可以省略语法 Path=。 这两个绑定是等效的:

    <Label Text="{Binding Path=Humidity, Source={StaticResource myWeatherService}}" />
    <Label Text="{Binding Humidity, Source={StaticResource myWeatherService}}" />
    

绑定目标为:

  • Label 控件。
  • 控件的 Text 属性。

显示 UI 时,{Binding} XAML 扩展会在 WeatherServiceLabel 之间创建绑定。 绑定将 WeatherService.Humidity 属性的值读入 Label.Text 属性。

将另一个控件用作绑定源

绑定的一个有用功能是可以绑定到其他控件。 以下 XAML 是一个简单的演示:

<VerticalStackLayout HorizontalOptions="Center" VerticalOptions="Center">
    <Label x:Name="TargetLabel" Text="TEXT TO ROTATE" BackgroundColor="Yellow" />
    <Slider WidthRequest="100" Maximum="360"
            Value="{Binding Rotation, Mode=OneWayToSource, Source={x:Reference TargetLabel}}" />
</VerticalStackLayout>

Slider.Value 属性绑定到 Label.Rotation 属性,但与之前介绍的方式不同。 此属性使用的是绑定模式 OneWayToSource,与典型的绑定机制相反。 与“源”更新“目标”相反,OneWayToSource 会在“目标”更改时更新“源”。。 此示例中,当滑块移动时,该绑定模式会根据滑块的值更新标签的旋转,如以下动画所示:

使用鼠标拖动的滑块控件的动画图像。当滑块移动时,一段文本将旋转以与滑块的位置相对应。

将控件绑定到另一个控件的典型情况是,控件(通常是 ListViewCarouselView 等集合控件)有一个要用作数据源的选定项。 在显示天气预报的页面示例中,可能有一个 ListView 显示五天的预报。 当用户在列表中选择一天时,该天气预报的详细信息会显示在其他控件中。 如果用户选择另一天,则其他控件将更新并显示所选日期的详细信息。

跨多个绑定使用相同的源

上一个示例演示了将静态资源用作单个绑定的源。 该源可用于多个绑定。 以下示例跨三个不同的控件声明绑定,这些控件都绑定到同一对象和属性 Path,但有些控件省略了 Path 属性:

<VerticalStackLayout Margin="10">
    <VerticalStackLayout.Resources>
        <vm:SimpleWeatherServiceObject x:Key="myWeatherService" />
    </VerticalStackLayout.Resources>
    <Entry Text="{Binding Humidity, Source={StaticResource myWeatherService}}" />
    <Label Text="{Binding Path=Humidity, Source={StaticResource myWeatherService}}" />
</VerticalStackLayout>

使用相同 Source 时不必使用相同的 Path

<VerticalStackLayout Margin="10">
    <VerticalStackLayout.Resources>
        <vm:SimpleWeatherServiceObject x:Key="myWeatherService" />
    </VerticalStackLayout.Resources>
    <Entry Text="{Binding Temperature, Source={StaticResource myWeatherService}}" />
    <Label Text="{Binding Path=Humidity, Source={StaticResource myWeatherService}}" />
</VerticalStackLayout>

很少会显示来自源的单条数据,但这种情况也有可能发生。 通常会有多个控件使用来自同一源的不同数据片段。 这种情况非常常见,以至于 BindableObject 类具有一个名为 BindingContext 的属性,该属性用作数据绑定的源。 请注意,.NET MAUI 控件继承自 BindableObject 类,因此 .NET MAUI 控件具有 BindingContext 属性。

设置绑定的 Source 是可选项。 未设置 Source 的绑定会自动在 XAML 可视化树中搜索 BindingContext,它在 XAML 中设置,或通过代码分配给父元素。 将按照以下模式评估绑定:

  1. 如果绑定定义了一个 Source,则使用该源并停止搜索。 将绑定的 Path 应用于 Source 以获取值。 如果未设置 Source,则会开始查找绑定源。

  2. 搜索从自身的目标对象开始。 如果目标对象的 BindingContext 不为 null,则将停止搜索,并将绑定的 Path 应用于 BindingContext 以获取值。 如果 BindingContext 为 null,则会继续搜索。

  3. 此进程一直持续到它到达 XAML 根为止。 将通过检查根目录的 BindingContext,寻找非 null 值来结束搜索。 如果未找到有效的 BindingContext,则该绑定没有绑定对象,也不会执行任何操作。

通常在根对象的级别设置 BindingContext,以应用于整个 XAML。

最后还有一个方便的功能值得一提。 绑定会监视对其源的对象引用所做的更改。 它甚至适用于将 BindingContext 用作其源的绑定。 如果将 SourceBindingContext 重新分配到其他对象,绑定会捕捉来自新源的数据并更新其目标。

知识检查

1.

关于 .NET MAUI 绑定中的源对象,哪一种说法是正确的?

2.

关于 .NET MAUI 绑定中的目标属性,那一种说法是正确的?

3.

如果 Grid 内控件上的所有绑定都需要同一源对象,那么只设置一次源对象的最安全策略是什么?