事件和事件处理在任何编程开发过程中都是一件特别繁琐的事情。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绑定在了一起。
其它的,我们下一篇再继续介绍吧。