事件和事件處理在任何編程開發過程中都是一件特別繁瑣的事情。Xamarin.From中提供的Data binding特性,可以將兩個對象的屬性自動關聯起來,從而極大地簡化事件處理的流程。此外,Data binding還是MVVM(Model-View-ViewModel)應用架構中非常核心的角色,是實現顯示與數據處理分離的關鍵特性。本文,我就與大家一起來學習Data binding的相關知識。
基礎知識
Data binding 相關的類、屬性及方法:
- Binding類,繼承自BindingBase,定義了數據綁定的許多特性;
- BindingContext屬性,由BindableObject定義;
- SetBinding方法,由BindableObject定義;
- BindableObjectExtensions類中定義了兩個SetBinding方法的重載;
支持XAML標志擴展的兩個類:
- BindingExtension類,為Xamarin.Forms私有,提供在XAML中使用Binding標識進行數據綁定的支持;
- ReferenceExtension類,這也是數據綁定的關鍵類;
與Data binding相關的兩個接口:
- INotifyPropertyChanged(在System.ComponentModel命名空間下),用于在屬性發生變化時發送通知的標準接口;
- IValueConverter(在Xamarin.Forms 命名空間下),用于在數據綁定中的類型轉換;
Data binding的核心思想:Data binding總是有一個源(source)和一個目標(target);源是某個對象的一個屬性,通常會在運行期間發生變化;當這個屬性變化時,Data binding將自動將這一變化更新到目標上,即另一個對象的一個屬性上。
有一點特別重要:Data binding中的目標必須是一個BindableProperty對象;
對源則沒有這樣的要求,因此源可以是一個普通的C#屬性;但在一般的數據綁定中,源的變化應當引起目標的變化,這種變化需要某種通知機制進行傳遞。這里有一個現成的機制—— INotifyPropertyChanged接口。INotifyPropertyChanged接口的內容非常簡單,僅僅定義了個PropertyChanged事件,這個事件在屬性變化時會被觸發;
public interface INotifyPropertyChanged {
event PropertyChangedEventHandler PropertyChanged;
}
因此,Data binding中的源必須實現INotifyPropertyChanged接口;
而BindableObject正好就實現了INotifyPropertyChanged接口,因此,只需將源定義成BindableObject對象即可,這樣它既可以成為源,也可以成為目標。而只實現INotifyPropertyChanged就只能作為源,不過,這么做也有好處,就是在實現上更簡單,我們后面再介紹只使用INotifyPropertyChanged的做法。
BindableObject和BindableProperty
那么BindableObject和BindableProperty是什么關系呢?
** BindableObject提供了對BindableProperty對象的支持,而BindableProperty是對CLR屬性的擴展。**
所謂的CLR屬性即是我們一般情況下使用的C#屬性,CLR是Common Language Runtiome通用語言運行時的縮寫。
在Xamarin.Form中,大多數的View、Layout和Page都是BindableObject對象。
以最簡單的Label為例,Label是一個BindableObject,其Text屬性是一個可綁定的CLR屬性。
我們一般是這樣使用Text屬性的。
Label label1 = new Label();
label1.Text = "Some Text";
但其實下面的做法是等效的:
Label label2 = new Label();
label2.SetValue(Label.TextProperty, "Some Text");
因為Text屬性的本質是這樣的:
public string Text
{
set { SetValue(Label.TextProperty, value); }
get { return (string)GetValue(Label.TextProperty);
}
其中SetValue和GetValue都是BindableObject中定義的方法。Label.TextProperty則一個BindableProperty對象。
//TextProperty在Label類中的聲明
public static readonly BindableProperty TextProperty;
可見,關于Text屬性的所有工作都是通過SetValue和GetValue完成的。而對Text的調用本質上都是對Label.TextProperty的調用,而Label.TextProperty為Text提供了一整套進行數據綁定的機制,因此Text也就成為了可綁定的屬性。
那么下面我們說一說如何定義BindableProperty對象。
假設我們要為一個類增加一個可綁定的TestProperty屬性,寫法大致如下:
public class MyClass : BindableObject
{
public static readonly BindableProperty TestProperty
=BindableProperty.Create("Test", //屬性名稱 propertyName
typeof(int), //返回類型 returnType
typeof(MyClass), // 聲明者的類型 declaringType
8, //默認值 defaultValue
...);
...
public int Test //同上面的屬性名稱一致
{
set { SetValue(TestProperty, value); }
get { return (int)GetValue(TestProperty); }
}
...
}
BindableProperty.Create方法其實還有6個可選參數:
其中defaultBindingMode是默認的綁定模式;validateValue是檢查值合法性的回調方法;propertyChanged和propertyChanging分別是值發生改變后和正上改變時觸發的回調方法;coerceValue是在值發生改變時,可以對值進行處理的回調方法;這三個方法的發生順序是coerceValue-》propertyChanging-》propertyChanged。而defaultValueCreator是可以根據需要提供不同默認值的回調方法,當然這個方法只發生在綁定的最開始。
其使用方法很簡單,假使現在有一個TestBindingable :ContentPage,我們定義的XAML如下:
...
<StackLayout>
<Label
Text="Text"
x:Name="label" />
<Button
Text="button_click"
Clicked="button_click" />
</StackLayout>
...
在C#代碼文件中,
...
MyClass myClass = new MyClass();
public TestBindingable() {
InitializeComponent();
label.BindingContext = myClass;
label.SetBinding(Button.FontSizeProperty, "Test");
}
void button_click(object sender, System.EventArgs e) {
myClass.Test += 2;
}
...
運行之后,我們每點擊一次button_click按鈕,就會發現Label中的字體變大一點,可見Label的FontSize屬性與myClass.Test綁定在了一起。
其它的,我們下一篇再繼續介紹吧。