深入浅出WPF(10)——“脚踩N条船”的多路Binding

简介:
小序:
 
通过前面几个章节的学习,我们已经了解了Data Binding的基本常识和简单的使用方法。今天让我们更进一步,学习一下多路Data Binding。
说实话,起“脚踩N条船”这个标题,实在有点不雅,可为了让大家记忆方便,我也管不了那么多鸟~~~那么什么是多路Binding、它有什么用、怎么用呢?
 
正文:
 
让我们分析这样一个需求——UI上有若干个文本框和一个“提交”按钮,这些文本框都是用户必须填写的,如果不都填写,提交按钮是不可用的。
 
习惯了使用WinForm的同学可能脑子里已经开始飞速地搜寻使用Event来解决的方案了。实际上,在WPF里使用多路Data Binding将非常简单。 所谓“多路Binding(也可以叫复合Binding)”就是指某个元素的Dependency Property的值不是由单一的数据源来决定,而是由多个数据源(通过一定逻辑)共同决定的,我们一般会把逻辑写在Converter里。是不是颇有些“脚踩N条船”的意思?
 
多路Binding使用的类是MultiBinding类,这个类实 际上就是对一组Binding对象的包装——本质上并没有影响Binding是“一对一”的基本理论。
 
下面让我们动手实现这个例子,因为大家已经对WPF的基本编程很熟悉了,所以我只把核心代码写在下面(又一次,我违反代码维护性原则,把它们写在了窗体的构造程序中)。
 
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Windows;
  6. using System.Windows.Controls;
  7. using System.Windows.Data;
  8. using System.Windows.Documents;
  9. using System.Windows.Input;
  10. using System.Windows.Media;
  11. using System.Windows.Media.Imaging;
  12. using System.Windows.Navigation;
  13. using System.Windows.Shapes;
  14. namespace MultiBindingSample
  15. {
  16.     public partial class Window1 : Window
  17.     {
  18.         public Window1()
  19.         {
  20.             InitializeComponent();
  21.             // 准备作为基础的“一对一”子Binding
  22.             Binding b1 = new Binding("Text") { Source = this.textBox1 };
  23.             Binding b2 = new Binding("Text") { Source = this.textBox2 };
  24.             Binding b3 = new Binding("Text") { Source = this.textBox3 };
  25.             Binding b4 = new Binding("Text") { Source = this.textBox4 };
  26.             // 准备一对一Binding的包装箱,并把子Binding装进去
  27.             MultiBinding mb = new MultiBinding();
  28.             mb.Bindings.Add(b1);
  29.             mb.Bindings.Add(b2);
  30.             mb.Bindings.Add(b3);
  31.             mb.Bindings.Add(b4);
  32.             // 为多路Binding配备决策逻辑(它是一个多路Converter),并设置为OneWay
  33.             mb.Converter = new SubmitEnableConverter();
  34.             mb.Mode = BindingMode.OneWay;
  35.             // 为Button设置多路Binding
  36.             this.button1.SetBinding(Button.IsEnabledProperty, mb);
  37.         }
  38.     }
  39.     public class SubmitEnableConverter : IMultiValueConverter // 注意Converter基类的变化
  40.     {
  41.         public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
  42.         {
  43.             // 使用Lambda表达式判断,只要有空的,就返回false
  44.             return !values.Cast<string>().Any(text => string.IsNullOrEmpty(text));
  45.         }
  46.         // 因为是只从数据源到目标的方向Binding,所以,这个函数永远也不会被调到
  47.         public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
  48.         {
  49.             throw new NotImplementedException();
  50.         }
  51.     }
  52. }
整个程序并没有什么难理解的,与我们以前学习的Binding最大的区别就在它的Converter上,这回使用的Converter,其基接口是IMultiValueConverter,而不是先前我们使用的IValueConverter。这个接口仍然只有两个函数需要实现一下。又因为我们这次使用的是OneWay模式,所以,代码中只有Convert函数中包含逻辑。
 
请大家注意,Convert函数中最重要的就是它的values参数。这个参数是一个数组,这个数组里包含的就是从一对一子Binding里送来的值(在我们的程序里,就是4个TextBox的Text属性值)。 数组是可被索引的,这就意味着values里面的值是有顺序的!那么这个顺序是什么呢? 这个顺序就是你调用MultiBinding.Bindings.Add(...)添加子Binding顺序—— 我认为这里是多路Binding一个小小的败笔——这样,写出来的代码会比较脆弱、顺序不能变,而且比较隐晦。换句话说,后来的程序员如果改变一下Add的顺序,就有可能导致程序出现很难测出的bug。
 
本例中,values[0]对应的是textBox1.Text属性值、values[1]对应的是textBox2.Text属性值、values[2]对应的是textBox3.Text属性值、values[3]对应的是textBox4.Text属性值。
 
然后,我使用Lambda表达式,判断了一下是否有某个TextBox值是空的,对这个值取反,就是Submit Button.IsEnable的值。
 
运行效果如下:
 
逻辑进阶
 
现在客户的需求变了。要求是,前两个文本框的内容一致、后两个文本框内容一致,这时候Submit才亮(这在注册新用户的时候经常遇到)。
 
要是在WinForm中使用Event实现,就会有多处事件处理函数有变更,而在这个例子中,我们只需改一两句代码就OK了。下面的代码中,我优化了格式——项目中推荐这样的格式。
 
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Windows;
  6. using System.Windows.Controls;
  7. using System.Windows.Data;
  8. using System.Windows.Documents;
  9. using System.Windows.Input;
  10. using System.Windows.Media;
  11. using System.Windows.Media.Imaging;
  12. using System.Windows.Navigation;
  13. using System.Windows.Shapes;
  14. namespace MultiBindingSample
  15. {
  16.     /// <summary>
  17.     /// Interaction logic for Window1.xaml
  18.     /// </summary>
  19.     public partial class Window1 : Window
  20.     {
  21.         public Window1()
  22.         {
  23.             InitializeComponent();
  24.             InitializeBindings(); // 拎出来封装
  25.         }
  26.         private void InitializeBindings()
  27.         {
  28.             // 声名、定义并初始化MultiBinding
  29.             MultiBinding mb = new MultiBinding() { Mode = BindingMode.OneWay, Converter = new SubmitEnableConverter() };
  30.             mb.Bindings.Add(new Binding("Text") { Source = this.textBox1 });
  31.             mb.Bindings.Add(new Binding("Text") { Source = this.textBox2 });
  32.             mb.Bindings.Add(new Binding("Text") { Source = this.textBox3 });
  33.             mb.Bindings.Add(new Binding("Text") { Source = this.textBox4 });
  34.             // 为Button设置多路Binding
  35.             this.button1.SetBinding(Button.IsEnabledProperty, mb);
  36.         }
  37.     }
  38.     // Converter
  39.     public class SubmitEnableConverter : IMultiValueConverter // 注意Converter基类的变化
  40.     {
  41.         public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
  42.         {
  43.             // 按对应值做决策
  44.             if (!values.Cast<string>().Any(text => string.IsNullOrEmpty(text))
  45.                 && values[0].ToString() == values[1].ToString()
  46.                 && values[2].ToString() == values[3].ToString())
  47.             {
  48.                 return true;
  49.             }
  50.             return false;
  51.         }
  52.         // 因为是只从数据源到目标的意向Binding,所以,这个函数永远也不会被调到
  53.         public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
  54.         {
  55.             throw new NotImplementedException();
  56.         }
  57.     }
  58. }
运行效果如下:
 
最后,希望大家玩儿的开心!也希望明天即将开幕的奥运会圆满、顺利!
 
OVER




本文转自 水之真谛 51CTO博客,原文链接:http://blog.51cto.com/liutiemeng/95293,如需转载请自行联系原作者
目录
相关文章
|
C#
WPF中Binding使用StringFormat格式化字符串方法
原文:WPF中Binding使用StringFormat格式化字符串方法 货币格式 // $123.46 货币格式,一位小数 // $123.5 前文字 //单价:$123.
2320 0
|
存储 自然语言处理 C#
WPF技术之Binding
WPF(Windows Presentation Foundation)是微软推出的一种用于创建应用程序用户界面的框架。Binding(绑定)是WPF中的一个重要概念,它用于在界面元素和数据源之间建立关联。通过Binding,可以将界面元素(如文本框、标签、列表等)与数据源(如对象、集合、属性等)进行绑定,从而实现数据的双向传递和同步更新。
297 2
WPF技术之Binding
WPF-Binding问题-模板样式使用Binding TemplatedParent与TemplateBinding区别
WPF-Binding问题-模板样式使用Binding TemplatedParent与TemplateBinding区别
253 0
|
C#
WPF QuickStart系列之数据绑定(Data Binding)
原文:WPF QuickStart系列之数据绑定(Data Binding) 这篇博客将展示WPF DataBinding的内容。 首先看一下WPF Data Binding的概览, Binding Source可以是任意的CLR对象,或者XML文件等,Binding Target需要有依赖属性。
1250 0
|
C#
解答WPF中ComboBox SelectedItem Binding不上的Bug
原文:解答WPF中ComboBox SelectedItem Binding不上的Bug 正在做一个打印机列表,从中选择一个打印机(System.Printing) var printServer = new LocalPrintServer(); PrintQueues = printServer.
1024 0
|
C#
WPF Binding Mode,UpdateSourceTrigger
原文:WPF Binding Mode,UpdateSourceTrigger WPF 绑定模式(mode) 枚举值有5个1:OneWay(源变就更新目标属性)2:TwoWay(源变就更新目标并且目标变就更新源)3:OneTime(只根据源来设置目标,以后都不会变)4:OneWayToSource...
1573 0
|
C# .NET 开发框架
WPF笔记 ( xmlns引用,Resource、Binding 前/后台加载,重新绑定) 2013.6.7更新
原文:WPF笔记 ( xmlns引用,Resource、Binding 前/后台加载,重新绑定) 2013.6.7更新 1、xmlns Mapping URI的格式是 clr-namespace:[;assembly=] (1)如果自定义类和XAML处在同一个Assembly之中,只还需要提供clr-namespace值。
1454 0
|
C# 数据格式 XML
【WPF】动态设置Binding的ConverterParameter转换器参数
原文:【WPF】动态设置Binding的ConverterParameter转换器参数 问题:XAML中,想要在一个Bingding语句中再次Bingding。
4634 0
|
C#
WPF的Binding学习笔记(一)
一、binding的一般步骤1,准备数据源    数据源需要实现INotifyPropertyChanged接口    例如: class Person : INotifyPropertyChanged {   public event PropertyChangedEventHandle...
745 0