一、使用ObjectDataProvider对象作为Binding的Source
理想的情况下,上游把类设计好、使用属性把数据暴露出来,下游程序员把这些类的实例作为Binding的source、把属性作为Binding的Path来消费这些类。但很难保证一个类的所有数据都使用属性暴露出来,比如我们需要的数据可能是方法的返回值,而修改底层类风险又恨大,这时候就需要使用ObjectDataProvider来包装作为Binding源的数据对象。
ObjectDataProvider,顾名思义就是把对象作为数据源提供给Binding。类似的还有XmlDataProvider,它们都是DataSourceProvider抽象类。
实例:
public class Calculator { public string Add(string arg1,string arg2) { double x = 0; double y = 0; double z = 0; if(double.TryParse(arg1,out x)&&double.TryParse(arg2,out y)) { z = x + y; return z.ToString(); } return "Input Error!"; } }
private void Button_Click(object sender, RoutedEventArgs e) { ObjectDataProvider odp = new ObjectDataProvider(); odp.ObjectInstance = new Calculator(); odp.MethodName = "Add"; odp.MethodParameters.Add("100"); odp.MethodParameters.Add("200"); MessageBox.Show(odp.Data.ToString()); }
把ObjectDataProvider当做Binding的Source来使用:
准备3个TextBox,第3个TextBox能够实时地显示数字的和。算法放在一个SetBinding的方法中,然后在窗体的构造函数里调用这个方法。
public MainWindow() { InitializeComponent(); this.SetBinding(); } public void SetBinding() { ObjectDataProvider odp = new ObjectDataProvider(); odp.ObjectInstance = new Calculator(); odp.MethodName = "Add"; odp.MethodParameters.Add("1"); odp.MethodParameters.Add("2"); //以ObjectDataProvider对象为Source创建Binding Binding bindingToArg1 = new Binding("MethodParameters[0]") { Source = odp, BindsDirectlyToSource = true, UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged }; Binding bindingToArg2 = new Binding("MethodParameters[1]") { Source = odp, BindsDirectlyToSource = true, UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged }; Binding bindingToResult = new Binding(".") { Source = odp }; //将Binding关联到UI元素上 this.textArg1.SetBinding(TextBox.TextProperty, bindingToArg1); this.textArg2.SetBinding(TextBox.TextProperty, bindingToArg2); this.textArg3.SetBinding(TextBox.TextProperty, bindingToResult); }
ObjectDataProvider类的作用是用来包装一个以方法暴露数据的对象,上面创建了一个ObjectDataProvider对象,然后用一个Calculator对象为其ObjectInstance属性赋值-这就把一个Calculator对象包装在了ObjectDataProvider对象里。
然后,使用MethodName属性指定将要调用的Calculator对象中名为Add的方法,如果有多个重载的Add方法,就向MethodName属性中加入多个该类性的对象,相当于告诉ObjectDataProvider对象去调用Calculator对象中具有两个string类型参数的Add方法,即MethodParameters是类型敏感的。
之后创建Binding,第一个Binding的Source是ObjectData对象、Path是ObjectDataProvider对象的MethodParameters属性所引用的集合中的第一个元素。BindsDirectlyToSource=true用于告诉Binding对象只负责把所从UI元素收集到的数据写入其直接Source而不是被ObjectDataProvider对象包装这的Calculator对象。
同事,UpdateSourceTrigger属性被设置为一有更新立即将值传回Source。
第二个Binding对象时第一个的翻版,只是把Path指向了第二个参数。第三个Binding对象仍然使用ObjectDataProvider对象作为Source,但使用.作为Path,因为数据源本身就是数据。
一般而言,数据从哪里来哪里就是Binding的Source,数据到哪里去哪里就应该是Binding的Target。示例中,前两个都以ObjectDataProvider作为数据源,但最后一个也以ObjectDataProvider作为数据源,前两个则在Binding的数据流向上做了限制,这样做的原因在于:
1、ObjectDataProvider的MethodParameters不是依赖属性,不作为Binding的目标
2、数据驱动UI的理念要求尽可能使用数据对象作为Binding的Source,而把UI元素当做Binding的Target。
二、使用Binding的RelativeSource
当一个Binding有明确的数据来源时,可以通过为Source或ElementName赋值的办法让Binding与之关联。但有些时候不确定作为Source对象的名字,但能够知道它与Binding模板的对象在UI布局上有相对关系,比如控件自己关联自己的某个数据、关联自己某级容器的数据,这时候就需要使用Binding的RelativeSource属性。
RelativeSource属性的数据类型为RelativeSource类,通过这个类的几个静态或非静态属性可以控制它搜索相对数据源的方式。
三、多路Binding
有时候UI要需要显式地信息由不止一个数据来源决定,这时候就需要使用MultiBinding,即多路Binding,MultiBinding与Binding一样均以BindingBase为基类。凡是能使用Binding对象的场合都能使用MultiBinding。MultiBinding具有一个名为Binding的属性,其类型是Collection<BindingBase>,通过这个属性MultiBinding把一组Binding对象聚合起来,处在这个集合中的Binding对象可以拥有自己的数据校验和转换机制,他们汇集起来的数据将共同决定传往MultiBinding目标的数据。