相关文章推荐
傲视众生的山寨机  ·  Use the XAML code ...·  1 周前    · 
刚毅的斑马  ·  Spring boot ...·  1 年前    · 

数据绑定允许链接两个对象的属性,以便一个对象中的更改会导致另一个对象发生更改。 这是一个非常有价值的工具,虽然可以在代码中完全定义数据绑定,但 XAML 提供了快捷方式和便利。 因此,其中最重要的标记扩展 Xamarin.Forms 之一是绑定。

数据绑定连接两个对象的属性,称为 目标 。 在代码中,需要执行两个步骤: BindingContext 必须将目标对象的属性设置为源对象,并且 SetBinding 通常与 Binding 类 (结合使用的方法) 必须在目标对象上调用,才能将该对象的属性绑定到源对象的属性。

目标属性必须是可绑定属性,这意味着目标对象必须派生自 BindableObject 。 联机 Xamarin.Forms 文档指示哪些属性是可绑定属性。 此类 Text 属性 Label 与可绑定属性 TextProperty 相关联。

在标记中,还必须执行代码中所需的相同两个步骤,但 Binding 标记扩展取代 SetBinding 调用和 Binding 类。

但是,在 XAML 中定义数据绑定时,可以通过多种方式设置 BindingContext 目标对象。 有时,它从代码隐藏文件设置,有时使用 StaticResource x:Static 标记扩展,有时作为属性元素标记的内容 BindingContext

绑定最常用于将程序视觉对象与基础数据模型连接,通常用于实现 MVVM (Model-View-ViewModel) 应用程序体系结构,如 第 5 部分所述。从数据绑定到 MVVM ,但可能采用其他方案。

视图到视图绑定

可以定义数据绑定以链接同一页上两个视图的属性。 在本例中,使用标记扩展设置 BindingContext 目标对象 x:Reference

下面是一个 XAML 文件,其中包含一 Slider 个视图和两 Label 个视图,其中一个视图由 Slider 值旋转,另一个视图显示 Slider 值:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamlSamples.SliderBindingsPage"
             Title="Slider Bindings Page">
    <StackLayout>
        <Label Text="ROTATION"
               BindingContext="{x:Reference Name=slider}"
               Rotation="{Binding Path=Value}"
               FontAttributes="Bold"
               FontSize="Large"
               HorizontalOptions="Center"
               VerticalOptions="CenterAndExpand" />
        <Slider x:Name="slider"
                Maximum="360"
                VerticalOptions="CenterAndExpand" />
        <Label BindingContext="{x:Reference slider}"
               Text="{Binding Value, StringFormat='The angle is {0:F0} degrees'}"
               FontAttributes="Bold"
               FontSize="Large"
               HorizontalOptions="Center"
               VerticalOptions="CenterAndExpand" />
    </StackLayout>
</ContentPage>

包含Slider使用x:Reference标记扩展的两Labelx:Name视图引用的属性。

绑定 x:Reference 扩展定义一 Name 个名为设置为所引用元素名称的属性,在本例 slider中。 但是, ReferenceExtension 定义标记扩展的 x:Reference 类还定义了一个 ContentProperty 属性 Name,这意味着它不是显式必需的。 只是为了多样性,第一个 x:Reference 包括“Name=”,但第二个不包含:

BindingContext="{x:Reference Name=slider}"
BindingContext="{x:Reference slider}"

Binding标记扩展本身可以有多个属性,就像和Binding类一样BindingBase。 for ContentPropertyBindingPath,但如果路径是标记扩展中的 Binding 第一项,则可以省略标记扩展的“Path=”部分。 第一个示例包含“Path=”,但第二个示例省略它:

Rotation="{Binding Path=Value}"
Text="{Binding Value, StringFormat='The angle is {0:F0} degrees'}"

这些属性都可以在一行上,也可以分隔成多行:

Text="{Binding Value,
               StringFormat='The angle is {0:F0} degrees'}"

做任何方便的事情。

StringFormat请注意第二个Binding标记扩展中的属性。 在中 Xamarin.Forms,绑定不执行任何隐式类型转换,如果需要将非字符串对象显示为字符串,则必须提供类型转换器或使用 StringFormat。 在后台,静态 String.Format 方法用于实现 StringFormat。 这可能是个问题,因为 .NET 格式规范涉及大括号,这些大括号也用于分隔标记扩展。 这会产生混淆 XAML 分析程序的风险。 为了避免这种情况,请将整个格式字符串放在单引号中:

Text="{Binding Value, StringFormat='The angle is {0:F0} degrees'}"

下面是正在运行的程序:

单个视图可以对其多个属性具有数据绑定。 但是,每个视图只能有一个 BindingContext,因此该视图上的多个数据绑定必须具有相同对象的所有引用属性。

解决此问题和其他问题涉及 Mode 属性,该属性设置为枚举的成员 BindingMode

  • Default
  • OneWay — 值从源传输到目标
  • OneWayToSource — 值从目标传输到源
  • TwoWay — 在源和目标之间双向传输值
  • OneTime— 数据从源到目标,但仅当更改时BindingContext
  • 以下程序演示了一种常见用法 OneWayToSourceTwoWay 绑定模式。 四Slider个视图旨在控制 Scalea Label的 、RotateRotateXRotateY属性。 起初,似乎这四个属性应该是数据绑定目标,因为每个属性 Label 都是由 a 设置 Slider的。 但是,BindingContextLabel只能是一个对象,并且有四个不同的滑块。

    因此,所有绑定都以看似向后的方式设置: BindingContext 四个滑块中的每一个都设置为该 Label滑块,绑定在滑块的属性上 Value 设置。 通过使用OneWayToSource和模式,这些Value属性可以设置源属性,这些属性是ScaleRotateRotateXRotateYLabelTwoWay

    <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 x:Class="XamlSamples.SliderTransformsPage"
                 Padding="5"
                 Title="Slider Transforms Page">
            <Grid.RowDefinitions>
                <RowDefinition Height="*" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="Auto" />
            </Grid.ColumnDefinitions>
            <!-- Scaled and rotated Label -->
            <Label x:Name="label"
                   Text="TEXT"
                   HorizontalOptions="Center"
                   VerticalOptions="CenterAndExpand" />
            <!-- Slider and identifying Label for Scale -->
            <Slider x:Name="scaleSlider"
                    BindingContext="{x:Reference label}"
                    Grid.Row="1" Grid.Column="0"
                    Maximum="10"
                    Value="{Binding Scale, Mode=TwoWay}" />
            <Label BindingContext="{x:Reference scaleSlider}"
                   Text="{Binding Value, StringFormat='Scale = {0:F1}'}"
                   Grid.Row="1" Grid.Column="1"
                   VerticalTextAlignment="Center" />
            <!-- Slider and identifying Label for Rotation -->
            <Slider x:Name="rotationSlider"
                    BindingContext="{x:Reference label}"
                    Grid.Row="2" Grid.Column="0"
                    Maximum="360"
                    Value="{Binding Rotation, Mode=OneWayToSource}" />
            <Label BindingContext="{x:Reference rotationSlider}"
                   Text="{Binding Value, StringFormat='Rotation = {0:F0}'}"
                   Grid.Row="2" Grid.Column="1"
                   VerticalTextAlignment="Center" />
            <!-- Slider and identifying Label for RotationX -->
            <Slider x:Name="rotationXSlider"
                    BindingContext="{x:Reference label}"
                    Grid.Row="3" Grid.Column="0"
                    Maximum="360"
                    Value="{Binding RotationX, Mode=OneWayToSource}" />
            <Label BindingContext="{x:Reference rotationXSlider}"
                   Text="{Binding Value, StringFormat='RotationX = {0:F0}'}"
                   Grid.Row="3" Grid.Column="1"
                   VerticalTextAlignment="Center" />
            <!-- Slider and identifying Label for RotationY -->
            <Slider x:Name="rotationYSlider"
                    BindingContext="{x:Reference label}"
                    Grid.Row="4" Grid.Column="0"
                    Maximum="360"
                    Value="{Binding RotationY, Mode=OneWayToSource}" />
            <Label BindingContext="{x:Reference rotationYSlider}"
                   Text="{Binding Value, StringFormat='RotationY = {0:F0}'}"
                   Grid.Row="4" Grid.Column="1"
                   VerticalTextAlignment="Center" />
        </Grid>
    </ContentPage>
    

    三个Slider视图上的绑定意味着SliderOneWayToSource值会导致其BindingContext属性发生更改,即Label命名label。 这三Slider个视图会导致更改RotateRotateXRotateY属性Label

    但是,属性的 Scale 绑定为 TwoWay. 这是因为该 Scale 属性的默认值为 1,并且使用 TwoWay 绑定会导致 Slider 初始值设置为 1 而不是 0。 如果绑定是 OneWayToSource,则 Scale 属性最初将从默认值设置为 0 Slider 。 这 Label 不可见,这可能会导致用户产生一些混淆。

    VisualElement 类还具有 ScaleXScaleY 属性,它们分别缩放 VisualElement x 轴和 y 轴。

    绑定和集合

    没有什么能比模板 ListView化更好地说明 XAML 和数据绑定的强大功能。

    ListView 定义 ItemsSource 类型的 IEnumerable属性,并显示该集合中的项。 这些项可以是任何类型的对象。 默认情况下, ListView 使用 ToString 每个项的方法显示该项。 有时这只是你想要的,但在许多情况下, ToString 只返回对象的完全限定类名。

    但是,可以通过使用模板以任何方式显示集合中的 ListView 项,该 模板涉及派生自 Cell的类。 模板针对模板中的每个 ListView项进行克隆,并且模板上设置的数据绑定将传输到单个克隆。

    通常,需要使用类为这些项目 ViewCell 创建自定义单元格。 此过程在代码中有点混乱,但在 XAML 中,它变得非常简单。

    XamlSamples 项目中包括一 NamedColor个名为 的类。 每个NamedColor对象都具有NameFriendlyName类型和属性string,以及一个Color类型Color属性。 此外,NamedColor还有 141 个与类中Xamarin.FormsColor定义的颜色对应的静态只读字段Color。 静态构造函数创建一个 IEnumerable<NamedColor> 集合,该 NamedColor 集合包含对应于这些静态字段的对象,并将其分配给其公共静态 All 属性。

    将静态NamedColor.All属性设置为ItemsSourceListView标记扩展非常简单x:Static

    <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 xmlns:local="clr-namespace:XamlSamples;assembly=XamlSamples"
                 x:Class="XamlSamples.ListViewDemoPage"
                 Title="ListView Demo Page">
        <ListView ItemsSource="{x:Static local:NamedColor.All}" />
    </ContentPage>
    

    生成的显示确定项的类型为 XamlSamples.NamedColor

    信息不多,但 ListView 可滚动且可选择。

    要为项定义模板,需要将属性拆分ItemTemplate为属性元素,并将其设置为一个,然后引用该DataTemplateViewCell属性。 对于属性,ViewViewCell可以定义一个或多个视图的布局以显示每个项。 下面是一个简单示例:

    <ListView ItemsSource="{x:Static local:NamedColor.All}">
        <ListView.ItemTemplate>
            <DataTemplate>
                <ViewCell>
                    <ViewCell.View>
                        <Label Text="{Binding FriendlyName}" />
                    </ViewCell.View>
                </ViewCell>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
    

    单元格的绑定源和单元格的子级是 ListView.ItemsSource 集合。

    元素 Label 设置为 View . ViewCell的属性。 (ViewCell.View 不需要标记, View 因为该属性 ViewCell是 .) 此标记显示 FriendlyName 每个 NamedColor 对象的属性:

    好多了。 现在,只需使用更多信息和实际颜色来启动项模板。 为了支持此模板,页面的资源字典中定义了一些值和对象:

    <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 xmlns:local="clr-namespace:XamlSamples"
                 x:Class="XamlSamples.ListViewDemoPage"
                 Title="ListView Demo Page">
        <ContentPage.Resources>
            <ResourceDictionary>
                <OnPlatform x:Key="boxSize"
                            x:TypeArguments="x:Double">
                    <On Platform="iOS, Android, UWP" Value="50" />
                </OnPlatform>
                <OnPlatform x:Key="rowHeight"
                            x:TypeArguments="x:Int32">
                    <On Platform="iOS, Android, UWP" Value="60" />
                </OnPlatform>
                <local:DoubleToIntConverter x:Key="intConverter" />
            </ResourceDictionary>
        </ContentPage.Resources>
        <ListView ItemsSource="{x:Static local:NamedColor.All}"
                  RowHeight="{StaticResource rowHeight}">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <StackLayout Padding="5, 5, 0, 5"
                                     Orientation="Horizontal"
                                     Spacing="15">
                            <BoxView WidthRequest="{StaticResource boxSize}"
                                     HeightRequest="{StaticResource boxSize}"
                                     Color="{Binding Color}" />
                            <StackLayout Padding="5, 0, 0, 0"
                                         VerticalOptions="Center">
                                <Label Text="{Binding FriendlyName}"
                                       FontAttributes="Bold"
                                       FontSize="Medium" />
                                <StackLayout Orientation="Horizontal"
                                             Spacing="0">
                                    <Label Text="{Binding Color.R,
                                           Converter={StaticResource intConverter},
                                           ConverterParameter=255,
                                           StringFormat='R={0:X2}'}" />
                                    <Label Text="{Binding Color.G,
                                           Converter={StaticResource intConverter},
                                           ConverterParameter=255,
                                           StringFormat=', G={0:X2}'}" />
                                    <Label Text="{Binding Color.B,
                                           Converter={StaticResource intConverter},
                                           ConverterParameter=255,
                                           StringFormat=', B={0:X2}'}" />
                                </StackLayout>
                            </StackLayout>
                        </StackLayout>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </ContentPage>
    

    请注意,用于 OnPlatform 定义行 BoxView 的大小和高度 ListView 。 尽管所有平台的值都是相同的,但可以轻松调整标记,以便其他值微调显示。

    绑定值转换器

    上一个 ListView 演示 XAML 文件显示结构的单个RGB属性Xamarin.FormsColor。 这些属性的类型 double 和范围为 0 到 1。 如果要显示十六进制值,则不能简单地与“X2”格式规范一起使用 StringFormat 。 这仅适用于整数,此外, double 值需要乘以 255。

    使用 值转换器(也称为 绑定转换器)解决了这一小问题。 这是一个实现接口的IValueConverter类,这意味着它具有两个命名ConvertConvertBack和方法。 当Convert值从源传输到目标时调用此方法;ConvertBack该方法用于从目标传输到源或OneWayToSourceTwoWay绑定中:

    using System;
    using System.Globalization;
    using Xamarin.Forms;
    namespace XamlSamples
        class DoubleToIntConverter : IValueConverter
            public object Convert(object value, Type targetType,
                                  object parameter, CultureInfo culture)
                double multiplier;
                if (!Double.TryParse(parameter as string, out multiplier))
                    multiplier = 1;
                return (int)Math.Round(multiplier * (double)value);
            public object ConvertBack(object value, Type targetType,
                                      object parameter, CultureInfo culture)
                double divider;
                if (!Double.TryParse(parameter as string, out divider))
                    divider = 1;
                return ((double)(int)value) / divider;
    

    该方法 ConvertBack 不在此程序中发挥作用,因为绑定只是从源到目标的一种方式。

    绑定引用具有该属性的 Converter 绑定转换器。 绑定转换器还可以接受使用 ConverterParameter 属性指定的参数。 对于一些多功能性,这就是指定乘数的方式。 绑定转换器检查转换器参数是否具有有效 double 值。

    转换器在资源字典中实例化,以便在多个绑定之间共享:

    <local:DoubleToIntConverter x:Key="intConverter" />
    

    三个数据绑定引用此单个实例。 请注意, Binding 标记扩展包含嵌入 StaticResource 的标记扩展:

    <Label Text="{Binding Color.R,
                          Converter={StaticResource intConverter},
                          ConverterParameter=255,
                          StringFormat='R={0:X2}'}" />
    

    下面是结果:

    处理 ListView 基础数据中可能发生的更改非常复杂,但前提是你采取某些步骤。 如果在运行时分配给 ItemsSource 更改属性的 ListView 项的集合(即,可以在集合中添加或删除项)对这些项目使用 ObservableCollection 类。 ObservableCollectionINotifyCollectionChanged实现接口,并ListView安装事件的CollectionChanged处理程序。

    如果项的属性在运行时发生更改,则集合中的项应实现 INotifyPropertyChanged 接口,并使用事件向属性值 PropertyChanged 发出更改信号。 本系列下一部分第 5 部分对此进行了演示 。从数据绑定到 MVVM

    数据绑定提供了一种强大的机制,用于在页面内的两个对象之间或视觉对象与基础数据之间链接属性。 但是,当应用程序开始使用数据源时,一种常用的应用程序体系结构模式开始作为一种有用的范例出现。 第 5 部分对此进行了介绍 。从数据绑定到 MVVM

  • XamlSamples
  • 第 1 部分。 使用 XAML (示例) 进行入门
  • 第 2 部分。 基本 XAML 语法 (示例)
  • 第 3 部分。 XAML 标记扩展 (示例)
  • 第 5 部分。 从数据绑定到 MVVM (示例)
  •