不拘一格用數據的Converter
上篇文已經說明,Binding就是數據源與目標之間的“關聯”。大多數情況下,數據從Source到Target以及從Target返回Source都是“直來直去”的,但有些場景卻需要我們對數據做些轉換才能為我所用。舉兩個典型的例子:
- 如果數據源里的值是Y和N,如果是Y,那么UI上的CheckBox就被勾選,否則就不勾選,這就需要我們把string(也許是char)類型的數據轉換成bool?類型再使用。如果Binding是TwoWay的,CheckBox的勾選操作還會把值傳回數據源。
- 如果“評論內容”TextBox里沒有內容,則“提交”Button不可以點擊。這是個典型的OneWay數據Binding,因為只有TextBox去影響Button的份兒。具體如何實現,大家可以先猜猜;)
想要實現這類的轉換,就需要為Binding這個“綠色通道”設置“關卡”,這里我們用到的關卡就是“數據轉換器”(Data Converter)。Converter實際上就是一個類,它這個類有個要求——它需要實現 IValueConverter 這個接口。這個接口的內容非常簡單——只有兩個方法,它們分別是:
- Convert方法 :按照你的要求,把從數據源傳來的數據轉成你想要的數據——至于是加減乘除還是煎炒炸燉,那就要看你怎么實現函數體了
- ConvertBack 方法:如果Binding是TwoWay的,那么數據目標會回傳經用戶改動后的數據,這時候你就不得不把數據轉換回數據源里的格式——大多數情況下,它是Convert方法的逆運算,具體情況還要具體分析。(不過,熟飯估計怎么著也變不成生米了,呵呵~~)
?下面是第一個例子的核心代碼,我來一步一步實現。
第一步 :先聲明一個類。我的習慣是 用Converter開頭,后綴是“源類型2目標類型” ,這里的“2”是“to”的意思。
?
- class ?ConverterYN2TF??
- {??
- ??
- }??
第二步 :讓這個類實現 IValueConverter 接口。這里有個使用VS2008的小竅門——在類名后寫上“: IValueConverter ”后,按下鍵盤上的“Shift+Alt+F10”會彈出VS2008的智能菜單,選擇其中的第一項“實現IValueConverter的方法”,VS2008會自動為我們生成需要實現的方法體:
- class ?ConverterYN2TF?:?IValueConverter??
- {??
- ????#region?IValueConverter?Members ??
- ??
- ???? public ? object ?Convert( object ?value,?Type?targetType,? object ?parameter,?System.Globalization.CultureInfo?culture)??
- ????{??
- ???????? throw ? new ?NotImplementedException();??
- ????}??
- ??
- ???? public ? object ?ConvertBack( object ?value,?Type?targetType,? object ?parameter,?System.Globalization.CultureInfo?culture)??
- ????{??
- ???????? throw ? new ?NotImplementedException();??
- ????}??
- ?
- ????#endregion ??
- }??
第三步 :添加Attribute。這步不是必需的,但加上有是有好處的——告訴Converter數據的源類型與目標類型各是什么。值得注意的是,CheckBox的IsChecked屬性是bool?類型的(可空bool類型),意思是說可以是True/False/Null三種值,表現在UI上就是勾選/不勾選/中間態。如果想讓CheckBox能顯示中間態,需要把它的IsThreeState屬性設為True。
- [ValueConversion( typeof ( string ),? typeof ( bool ?))]? //數據的源類型是string,目標類型是bool? ??
- class ?ConverterYN2TF?:?IValueConverter??
- {??
- ?
- ????#region?IValueConverter?Members ??
- ??
- ???? public ? object ?Convert( object ?value,?Type?targetType,? object ?parameter,?System.Globalization.CultureInfo?culture)??
- ????{??
- ???????? throw ? new ?NotImplementedException();??
- ????}??
- ??
- ???? public ? object ?ConvertBack( object ?value,?Type?targetType,? object ?parameter,?System.Globalization.CultureInfo?culture)??
- ????{??
- ???????? throw ? new ?NotImplementedException();??
- ????}??
- ?
- ????#endregion ??
- }??
第四步:實現這兩個方法。在開始動手前,我們先分析一下這兩個方法的參數和返回值。
首先,這兩個方法的參數和返回值都是object類型的,之所以這樣做,是因為接口的設計者并不知道你要傳入和傳出的數據是什么類型的,只好用它們“絕對正確”的基類——object了。
其次,對于Convert方法來說,?value是從數據源傳來的數據,返回值是轉換好后發送給數據目標的數據。對于ConvertBack方法正好反過來,value是從數據目標(比如UI)傳回來的數據,返回值是要與數據源匹配的數據。
再次,偶爾我們會用到parameter那個參數。比如在轉換某些數據的時候,我們需要依賴一些其它的外部數據來輔助我們的數據轉換,這時候就可以在parameter上打主意了。如果想傳多個參數的話,可以把這些參數打包成數組或者class/struct等數據結構再傳進來。在我們工作的代碼中用到過一次parameter,我為我的Converter類準備了一個帶參數的構造函數,把外部的輔助數據傳給Converter
最后,如果你的Binding是OneWay的,那么恭喜你——你的ConvertBack函數體隨便怎么實現都可以——因為它不可能被調用。
完成的類是這樣的:
- [ValueConversion( typeof ( string ),? typeof ( bool ?))]? //數據的源類型是string,目標類型是bool? ??
- class ?ConverterYN2TF?:?IValueConverter??
- {??
- ???? public ? object ?Convert( object ?value,?Type?targetType,? object ?parameter,?System.Globalization.CultureInfo?culture)??
- ????{??
- ???????? string ?str?=?System.Convert.ToString(value);??
- ???????? switch ?(str)??
- ????????{??
- ???????????? case ? "Y" :??
- ???????????????? return ? true ;??
- ???????????? case ? "N" :??
- ???????????????? return ? false ;??
- ???????????? default :??
- ???????????????? return ? null ;??
- ????????}??
- ????}??
- ??
- ???? public ? object ?ConvertBack( object ?value,?Type?targetType,? object ?parameter,?System.Globalization.CultureInfo?culture)??
- ????{??
- ???????? bool ??b?=?System.Convert.ToBoolean(value);??
- ???????? switch ?(b)??
- ????????{??
- ???????????? case ? true :??
- ???????????????? return ? "Y" ;??
- ???????????? case ? false :??
- ???????????????? return ? "N" ;??
- ???????????? default :??
- ???????????????? return ? "Null" ;??
- ????????}??
- ????}??
- }??
使用這個類的方法是將Binding實例的Converter屬性設置為這個類的一個實例:
- checkBox1.IsThreeState?=? true ;??
- Binding?binding?=? new ?Binding( "Text" );??
- binding.Source?=?textBox1;??
- binding.Converter?=? new ?ConverterYN2TF();? //?設定Converter ??
- this .checkBox1.SetBinding(CheckBox.IsCheckedProperty,?binding);??
?
至于上面的第二個例子,留給大家自己動手去實現吧。想一想:怎樣才能讓Button的IsEnable屬性與TextBox中文本的有無關聯上呢??
? 讓數據“干干凈凈”的Validation
再讓我們來看看如何對數據進行“安檢”。?
首先,這里有一個“霸王條款”——Binding認為從數據源出去的數據都是“干凈”的,所以不進行校驗;只有從數據目標回傳的數據才有可能是“臟”的,需要校驗。
其次,對于一個Binding而言,Converter只能有一個,而校驗條件可以是好幾個——它們存儲在Binding的ValidationRules這個集合里。其實,數據校驗與轉換做的事兒差不多。
下面給出一個例子:我們以一個Slider為數據源,它的滑塊可以從Value=0滑到Value=100;同時,我們以一個TextBox為數據目標,并通過Validation限制它只能將20到35之間的數據傳回數據源。現實當中恐怕很少有這么干的,我們這個例子只是為了說明校驗的使用方法:)
若要創建一個自定義的校驗條件,需要聲明一個類,并讓這個類派生自ValidationRule類。ValidationRule只有一個名為Validate的方法需要我們實現,這個方法的返回值是一個ValidationResult類型的實例——這個實例攜帶著兩個信息:
- bool類型的 IsValid屬性告訴Binding回傳的數據是否合法
- object類型(一般是存儲一個string)的ErrorContent屬性告訴Binding一些信息,比如當前是進行什么操作而出現的校驗錯誤等等,一般我會把這些信息寫進Log文件里
實現好的類是這樣的:
- public ? class ?MyValidationRule?:?ValidationRule??
- {??
- ???? public ? override ?ValidationResult?Validate( object ?value,?System.Globalization.CultureInfo?cultureInfo)??
- ????{??
- ???????? double ?d?=?0.0;??
- ???????? if ?( double .TryParse(( string )value,? out ?d)?&&?d?>=?20?&&?d?<=?35)??
- ????????{??
- ???????????? return ? new ?ValidationResult( true ,? "OK" );??
- ????????}??
- ???????? else ??
- ????????{??
- ???????????? return ? new ?ValidationResult( false ,? "Error" );??
- ????????}??
- ????}??
- }??
在代碼里這樣使用它:
- Binding?binding?=? new ?Binding( "Value" );??
- binding.Source?=?slider1;??
- binding.UpdateSourceTrigger?=?UpdateSourceTrigger.PropertyChanged;??
- binding.ValidationRules.Add( new ?MyValidationRule());? //?加載校驗條件 ??
- textBox1.SetBinding(TextBox.TextProperty,?binding); ?
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
