在 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 重新分配到其他对象,绑定会捕捉来自新源的数据并更新其目标。