1 简述数据绑定

数据绑定(Data Binding)常用于将程序中的数据对象绑定到UI上,当程序中数据发生变化时让UI上显示的内容也跟着变化,或者当用户在UI上操作时就将程序中的后台数据也同步变化。Vue中也有类似的功能,只是Vue默认就做了双向数据绑定,但是Avalonia和WPF中不是这样,而且需要手动注册通知才能在数据set时候通知使用者(从这个角度看又很像是Qt的“信号槽”的封装)。

使用数据绑定往往和MVVM(Model-View-ViewModel)模式的使用分不开,也就是说将M绑定到V(或者反过来,如果两边都做那就是双向数据绑定)。

2 DataContext

2.1 简述DataContext

在Avalonia中视图层有两种组件,一种是Window,一种是UserControl,在VS里添加项时在Avalonia下可以选,这和WPF差不多。

两者都是Avalonia.Controls命名空间下的,且都继承自ContentControl类,而ContentControl类又继承自Control类,这个类向上有一个父类StyledElement集成了DataContext这个属性(不是字段)。

它的作用在于,在View层做数据绑定时默认绑定到这个属性所指定的对象上。例如在App.xaml.cs中可以看到创建MainWindow时,创建了其对应的ViewModel对象,并传入了DataContext这个属性中:

desktop.MainWindow = new MainWindow
{
	DataContext = new MainWindow_VM(),
};

那么在View层中,这里也就是在MainWindow.xaml中使用数据绑定时,就会绑定到上面new MainWindow_VM()创建出的那个对象的相应属性上去,例如:

<Window ...
        Content="{Binding Content}">
        <!-- 绑定Window.DataContext.Content -->
</Window>

就是将这个MainWindow.xamlContent(实际上也就是双标签里面的内容),绑定自其DataContext属性的对象(这里即是刚刚创建的MainWindow_VM类的对象)的Content属性。

2.2 为其嵌套DataTemplate

当为控件的*Template属性(如ContentTemplateItemTemplate)设置为DataTemplate即数据模板时,会自动为DataTemplate内的控件设置DataContext属性,例如在某UserControl中:

<!-- 绑定UserControl.DataContext.Items -->
<ItemsControl Items="{Binding Items}">
	<ItemsControl.ItemTemplate>
		<DataTemplate>
		<!-- 绑定UserControl.DataContext.Items中每一个对象的IsChecked属性和Description属性 -->
			<CheckBox Margin="4" IsChecked="{Binding IsChecked}" Content="{Binding Description}" />
		</DataTemplate>
	</ItemsControl.ItemTemplate>
</ItemsControl>

又如官方文档上ContentControl的例子

3 属性/集合变化通知

在属性发生变化时要通知绑定它的地方也跟着变化,最原始的方式就是继承INotifyPropertyChanged的方式。这里用ReactiveUI实现,要继承封装好的ReactiveObject类,然后在完整属性(含私有字段)的set中调用:

this.RaiseAndSetIfChanged(ref 私有字段, value);

集合变化还是直接用.Net的ObservableCollection,或者ReactiveUI的ReactiveList,详见官方文档说明。

4 在xaml中使用{Binding xxx}标记扩展

这里和WPF中的用法一样;如"{Binding Name}""{Binding Path=Name}"等价,表示绑定DataContext的Name属性;又如"{Binding}""{Binding .}"一样,表示使用DataContext本身作为绑定源。在绑定的Path中遇到可迭代的内容,可以用下标选择,如"{Binding Students[0].Name}"

使用Mode可以指定绑定的方式,如<TextBox Text="{Binding Name, Mode=TwoWay}">就将文本框和后台DataContext的Name字段做了双向绑定。更多Mode见官方文档说明。

5 从其它位置绑定

默认时,绑定的是DataContext的属性,可以手动指定绑定到其它位置。

5.1 绑定一个提供了Name的控件

可以在标记语法中使用ElementName指定绑定的控件的Name:

<TextBox Name="other">
<TextBlock Text="{Binding Text, ElementName=other}"/>

也可以简写成#Name的形式,类似于CSS选择器:

<TextBox Name="other">
<TextBlock Text="{Binding #other.Text}"/>

后者是WPF里不具备的写法。

5.2 绑定父组件

所有同文件内的绑定都可以用5.1中的做法(只要给个Name就行了),这里用$parent选择器就可以向父组件选择,其中可以在[]内用数字表示向上的层级,向上一层就是0;可以在[]内用类型名指定向上搜索的类型,详见官方文档的说明。

更多时候,这个选择器应该用在绑定整个xaml文件的父组件,例如在官方文档教程——待办列表(TodoList)的TodoListView里,点击Add按钮的命令是去调用MainWindowViewModel.csAddItem()方法,而这放在MainWindow的DataContext中,而MainWindow又是这个TodoListView的父组件,且在这个文件中体现不出来,所以要用$parent[Window].DataContext.AddItem向上搜索。

6 绑定对象的转换

当绑定源的数据要给xaml使用时,可能需要对数据进行处理,将其类型、格式转换掉。

6.1 取反

这个和Vue里或者是很多模板语言里一样,直接用!就可以对数据进行取反,如果数据本身不是bool类型的,使用!实际上就是先调用Convert.ToBoolean将其转换成布尔数据然后再取反,所以!!可以起到将数据转换成布尔类型的作用。

6.2 自建的Converter

使用转换器(Converter)可以实现更复杂的类型转换和处理,要自建Converter,要有一个继承IValueConverter接口的类(在Avalonia中这个接口被放在Avalonia.Data.Converters命名空间下,而不是System.Windows.Data下),然后实现其中的ConvertConvertBack方法。这里先不深究了。

6.3 内置的Converter

官方文档中的描述。

Logo

前往低代码交流专区

更多推荐