• WPF MultiBinding


    让我们分析这样一个需求——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. }

    运行效果如下:

     

     

    文章来源:http://www.cnblogs.com/luohengstudy/admin/EditPosts.aspx?opt=1

  • 相关阅读:
    25-k个一组翻转链表 203-移除链表元素 206-反转一个链表
    24. 两两交换链表中的节点
    23-合并K个升序链表
    19-删除链表的倒数第N个节点
    18-四数之和
    21-合并两个有序链表
    双指针
    16-最接近的三数之和
    15-三数之和
    RobotFramework 断言关键字
  • 原文地址:https://www.cnblogs.com/luohengstudy/p/3096318.html
Copyright © 2020-2023  润新知