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.xaml
的Content
(實際上也就是雙標籤裏面的內容),綁定自其DataContext
屬性的對象(這裏即是剛剛創建的MainWindow_VM
類的對象)的Content
屬性。
2.2 爲其嵌套DataTemplate
當爲控件的*Template
屬性(如ContentTemplate
、ItemTemplate
)設置爲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.cs
的AddItem()
方法,而這放在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
下),然後實現其中的Convert
和ConvertBack
方法。這裏先不深究了。
6.3 內置的Converter
見官方文檔中的描述。