MAUI新生2.1-数据绑定和MVVM:数据绑定Binding

本章节内容较多,目录如下:

  1. 基本概念
  2. 数据绑定的五种实现方式
  3. 绑定路径Path
  4. 绑定模式BindingMode
  5. 绑定数据源的字符串格式化StringFormat
  6. 绑定源设置为自身或上级RelativeSource
  7. 绑定源不存在链接属性的回退值或链接属性值为Null时的替换值
  8. 编译绑定

 

  

一、基本概念

1、数据绑定,链接两个对象的属性,其中一个对象作为绑定源,另一个对象作为绑定目标。两个链接的属性之间,可以实现双向、单向或单次等丰富的数据响应,一方数据发生变化,另一方做出同步响应。绑定目标几乎总是页面元素,而绑定源可能是同一页面元素,也可能是后台代码文件中某一对象。

2、数据绑定的另一个显著特征是,绑定源对绑定目标是无感的,它不需要知道哪些目标绑定了它。绑定源主要有两种类型,一是可绑定对象(BindableObject)的可绑定属性(BindableProperty),绝不多数控件都是可绑定对象,绝不多数控件属性都是可绑定属性;二是MVVM模式中的ViewModel,可以作为绑定源,通过Propertychanged事件机制实现绑定。

3、绑定设置主要在绑定目标上进行,主要完成以下两个工作:①通过设置BindingContext,或者Binding扩展标记的Source属性,设置绑定源;②通过Binding扩展标记设置绑定的Path(绑定源的链接属性)。BindingContext(绑定上下文)比较特殊,可以将绑定上下文设置在控件树的上级元素上,绑定目标会延着控件树向上找,以最先找到的上下文为准,这也叫绑定上下文继承。

 

 

二、数据绑定的五种实现方式(以同一页面元素之间的绑定为例

以下案例实现Lable/文本控件的Text值,与Slider/滑块控件的Value值绑定。Lable是绑定目标,Slider是绑定源。提供了5种实现方式。 

<!--实现方式①:BindingContext和Binding都在绑定目标上设置-->
<VerticalStackLayout>
    <!--StringFormat实现格式化字符串输出。{0:F0},其中0表示变量占位符,F0表示小数位为0位的浮点值-->
    <Label BindingContext="{x:Reference slider}" Text="{Binding Path=Value, StringFormat='滑块值为:{0:F0}'}" />
    <Slider x:Name="slider" Maximum="360" />
</VerticalStackLayout>


<!--实现方式②:BindingContext上控制树的上级元素中设置-->
<VerticalStackLayout BindingContext="{x:Reference slider}">
    <!--Path是内容属性,可以省略,即{Binding Value}-->
    <Label Text="{Binding Path=Value, StringFormat='滑块值为:{0:F0}'}" />
    <Slider x:Name="slider" Maximum="360" />
</VerticalStackLayout>


<!--实现方式③:直接在Binding扩展标记上设置绑定源-->
<VerticalStackLayout>
    <Label Text="{Binding Source={x:Reference slider}, Path=Value, StringFormat='滑块值为:{0:F0}'}" />
    <Slider x:Name="slider" Maximum="360" />
</VerticalStackLayout>


<!--实现方式④:通过后台代码设置数据绑定,使用BindingContext-->
public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();
        label.BindingContext = slider;
        label.SetBinding(Label.TextProperty,"Value",stringFormat:"滑块值为:{0:F0}");
    }
}


<!--实现方式⑤:通过后台代码设置数据绑定,不使用BindingContext-->
public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();
        label.SetBinding(Label.TextProperty,new Binding("Value",source:slider,stringFormat:"滑块值为:{0:F0}"));
    }
}

 

 

 

三、绑定路径Path:通过绑定路径,选择绑定源的链接属性。如果属性为复杂类型或带索引的集合类型,可以通过点运算符或索引运算符选择子属性

<!--  绑定TimePicker的Time属性的子属性Hours  -->
<VerticalStackLayout HorizontalOptions="Center">
    <Label
        x:Name="label"
        BindingContext="{x:Reference timePicker}"
        Text="{Binding Time.Hours}" />
    <TimePicker x:Name="timePicker" />
</VerticalStackLayout>


<!--  绑定源为ContentPage,Content属性为VerticalStackLayout  -->
<!--  VerticalStackLayout的Children属性是一个集合,通过索引获得第label1  -->
<!--  最后绑定到label1的Text属性的Length属性  -->
<ContentPage
    x:Class="MauiApp8.MainPage"
    xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    x:Name="page">

    <VerticalStackLayout HorizontalOptions="Center">
        <Label x:Name="label1" Text="HelloWorld" />
        <Label x:Name="label2" TText="{Binding Source={x:Reference page}, Path=Content.Children[0].Text.Length}" />
    </VerticalStackLayout>

</ContentPage>

 

 

 

四、绑定模式BindingMode:通过设置绑定模式,可以规定两个链接属性之间数据响应的方式,有以下几种方式:

  • TwoWay:数据在源和目标之间双向移动
  • OneWay:数据从源流向目标
  • OneWayToSource:数据从目标流向源
  • OneTime:数据从源流向目标,但仅在更改BindingContext时,响应一次
  • Default:默认值,大多数绑定目标的属性是OneWay,少部分是TwoWay,如:Editor/Entry/SercherBar/EntryCell的Text属性,DatePicker的Date属性,TimePicker的Time属性,Picker的SelectedIndex和SelectedItem属性,Slilder和Stepper的Value属性,Switch的IsToggled属性,SwitchCell的On属性,MultiPage的SelectedItem属性,ListView的IsRefreshing属性,其中大部分是表单类控件。
<!--在XAML中设置-->
<VerticalStackLayout>
    <Label
        x:Name="label"
        BindingContext="{x:Reference slider}"
        Scale="{Binding Value, Mode=TwoWay}"
        Text="HelloWorld!"/>
    <Slider x:Name="slider" Maximum="360" />
</VerticalStackLayout>


<!--在后台代码C#中设置-->
public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();
        label.BindingContext = slider;
        label.SetBinding(Label.ScaleProperty, "Value", BindingMode.TwoWay);
    }
}

 

 

 

五、绑定数据源的字符串格式化StringFormat

如果绑定目标的属性为字符串,且绑定模式为OneWay或TwoWay,可以使用StringFormat对数据源进行格式化。<Label Text="{Binding Value,StringFormat='The slider value is {0:F2}'}" /> 。如上例,{0:F2},其中0为绑定源数据值的占位符号,F2为输出格式。需要注意的是,即使绑定模式为TwoWay,使用StringFormat后,数据仅从源流向目标。

 

 

六、绑定源设置为自身或上级RelativeSource(相对于自己的源)

(1)绑定到自身

<!--WidthRequest绑定元素自身的HeightRequest属性-->
<VerticalStackLayout>
    <Slider x:Name="slider" Maximum="500" />
    <BoxView
        CornerRadius="20"
        HeightRequest="{Binding Source={x:Reference slider}, Path=Value}"
        WidthRequest="{Binding Source={RelativeSource Self}, Path=HeightRequest}"
        Color="LightBlue" />
</VerticalStackLayout>


<!--ListView继承ContentPage的BindingContext-->
<!--BindingContext通过Binding,绑定到自身的DefaultViewModel属性上-->
<!--DefaultViewModel,通过后台代码定义-->
<ContentPage ...
             BindingContext="{Binding Source={RelativeSource Self}, Path=DefaultViewModel}">
    <StackLayout>
        <ListView ItemsSource="{Binding Employees}">
            ...
        </ListView>
    </StackLayout>
</ContentPage>

 

(2)绑定到上级 

<!--绑定到上级的指定类型元素AncestorType-->
<!--如果指定类型有多个,则指定上级元素的第几层AncestorLevel-->
<!--绑定上级使用较少,因为可以直接通过x:Reference绑定指定元素-->
<Label Text="{Binding Source={RelativeSource AncestorType={x:Type Entry}, AncestorLevel=2}, Path=Text}" />

 

 (3)控件模板将BindingContext绑定到应用该模板的对象,在控件模板中详述

<!--相当于控件模板中的元素的属性值,在使用控件模板时传递,而不是在定义控件模板时硬编码-->
<ContentPage ...>
    <ContentPage.Resources>
        <ControlTemplate x:Key="CardViewControlTemplate">
            <Frame BindingContext="{Binding Source={RelativeSource TemplatedParent}}"
                   BorderColor="{Binding BorderColor}"
                   ......>
                   ......
            </Frame>
        </ControlTemplate>
    </ContentPage.Resources>
    <StackLayout>        
        <controls:CardView BorderColor="DarkGray"
                           ......
                           ControlTemplate="{StaticResource CardViewControlTemplate}" />
    </StackLayout>
</ContentPage>

 

 

 

七、绑定源不存在链接属性的回退值或链接属性值为Null时的替换值

<!--Entry不存在TextAbsent属性,label1通过FallbackValue设置回退值-->
<!--页面初始化时,Entry的Text为Null,label2通过TargetNullValue设置替换值-->
<StackLayout Padding="30">
        <Entry x:Name="entry" Placeholder="请输入"/>
        <Label x:Name="label1" Text="{Binding Source={x:Reference entry}, Path=TextAbsent, FallbackValue='绑定源不存在TextAbsent属性'}" />
        <Label x:Name="label2" Text="{Binding Source={x:Reference entry}, Path=Text, TargetNullValue='未输入值'}" />
    </StackLayout>

 

 

 

八、通过编译绑定,使用编译时绑定校验功能,并提升绑定性能。x:Datatype

XAML的绑定在运行时解析,造成两个问题:一是无法对绑定进行编译时校验,要到运行时才能发现;二是存在性能损耗。x:Datatype的作用,就是在编译时,明确绑定上下文的类型,可以明显改善以上两个问题,称之为编译时绑定。在XAML元素属性上声明x:Datatype后,绑定作用会向下延伸到子元素。所以一般情况下,x:Datatype和BindingContext定义在同一层级的元素上,但也可以在视觉树的任一层级声明。除了和BindingContext一起使用外,x:Datatype还可作用于DataTemplate,使集合绑定也变成编译时。

<!--BindingContext和x:Datatype都定义在ContentPage元素上-->
<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="MauiApp7.MainPage"
             x:DataType="local:HslColorViewModel">

    <ContentPage.BindingContext>
        <local:HslColorViewModel Color="Sienna" />
    </ContentPage.BindingContext>

    <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> 

</ContentPage>

<!--在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="MauiApp7.MainPage"
             x:DataType="local:HslColorViewModel">

    <Grid>
        <ListView 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>
    </Grid>    

</ContentPage>

 

posted @ 2022-11-26 23:24  functionMC  阅读(2288)  评论(0编辑  收藏  举报