深入淺出WPF(8)——數據的綠色通道,Binding(中)

深入淺出WPF(8)——數據的綠色通道,Binding(中)

小序:

今天中午吃完飯回工位的路上,和俺們組資深的Level 2技術支持肖老師聊了幾句。我跟肖老師說,最近我在學習Binding,肖老師說——那可不是個好東西!因爲如果在程序中使用了Binding,當出現錯誤的時候,比較難於調試。道理很簡單——以前使用事件(C++裏是回調)的時候,能明確地在事件處理函數裏去跟蹤調試,現在使用Binding,數據源和UI之間是一支封閉的“管道”,在代碼中很難看到他們是在哪裏關聯上的、出了問題也不知道在哪裏攔截、設斷點。

這的確是個問題!我又去請教了別的同事。Allen同學告訴我,在Binding和數據目標(的Dependency Property)有一些事件,會在數據有傳輸的時候被激發,或者在Binding的Converter和Validator上下下功夫。看來Binding值得研究的地方還真多呀!

文章的篇幅畢竟有限,我只能撿工作當中用的最多的來介紹。那麼,我還有哪些東西需要介紹呢?

  • 讓數據“爲我所用”的Converter
  • 讓數據“乾乾淨淨”的Validation
  • 集合控件與集合數據的Binding
  • 偷懶專用的數據中轉站——DataContext

希望我的工作能給大家搭起一個良好的學習框架——全面細緻的內容盡在MSDN裏,請大家閱讀的時候會輕鬆一些。

正文 

不拘一格用數據的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);

程序執行起來之後的效果是這樣的:

我可以使用Slider滑出從0到100的值來,也可以使用TextBox輸入20到35之間的值;但當我輸入小於20或者大於35的數字以及非數字時,值就不會被傳回到Slider(數據源),同時,TextBox還會被一個紅色的邊框圈起來以示警告。至於警告的風格,我們可以自定義,具體怎麼定義我會放在Style一節去叨叨。

 

TO BE CONTINUE...

下篇文中我們將介紹非常實用的集合Binding與懶人的最愛——DataContext

 

小知識:什麼是L2

CA有些同事(而且是C++比較牛的同事)是做Level 2的,也就是“二級技術支持”,Level 1是接客戶電話、對客戶進行支持的——他們最重要的工作就是當用戶發現bug時用開心的口吻告訴他們:“Hi,that a feature:)”,呵呵,開個玩笑,實際上他們經常面對怒氣衝衝、快要發瘋的客戶。如果真的是個bug,那L1的同學們也搞不定,這時候就要把bug提交給L2的同學們了,L2的同學們會連夜改代碼、解bug、出SP包……算是我們的“救火隊員”了,公司的良好信譽離不開他們的努力。

======================================================

廣而告之:

  1. 急聘.NET開發3~5年經驗者3名,C++開發3~5年經驗者3名!有意者請將簡歷發送至[email protected],或在我的空間留言。
  2. 在下長期幫助朋友招聘.NET開發/C++開發/Java開發/軟件測試俊傑人才,有意者請將簡歷發送至[email protected],或在我的空間留言。我的空間同時也有不少獵頭朋友關注,機會也很多。
  3. 在下可以在業餘時間提供.NET開發的入門和初級培訓。對於非營利機構及與學生學習就業相關的社團組織,本人將提供免費培訓服務
  4. 轉載本文,請將上述廣告一併轉載,謝謝!  

 

 

 

 

 

 

 

發佈了145 篇原創文章 · 獲贊 37 · 訪問量 99萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章