MAUI新生2.1-数据绑定和MVVM:数据绑定Binding
本章节内容较多,目录如下:
- 基本概念
- 数据绑定的五种实现方式
- 绑定路径Path
- 绑定模式BindingMode
- 绑定数据源的字符串格式化StringFormat
- 绑定源设置为自身或上级RelativeSource
- 绑定源不存在链接属性的回退值或链接属性值为Null时的替换值
- 编译绑定
一、基本概念
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>
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!