深刻淺出WPF(10)——「腳踩N條船」的多路Binding

深刻淺出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
相關文章
相關標籤/搜索