Effective C# 定製和支持數據綁定

有經驗的Windows程序員一定對寫代碼從一個控件上取值,以及把值存儲到控件上很熟悉:

 

 

public Form1 : Form
{
private MyType myDataValue;
private TextBox textBoxName;

private void InitializeComponent( )
{
    textBoxName.Text 
= myDataValue.Name;
    
this.textBoxName.Leave += new
      System.EventHandler( 
this.OnLeave );
}


private void OnLeave( object sender, System.EventArgs e )
{
    myDataValue.Name 
= textBoxName.Text;
}

}

 

這太簡單了,正如你知道的,重複代碼。之所以不喜歡這樣重複代碼,就是因爲應該有更好的方法。是的,.Net框架支持數據綁定,它可以把一個對象的屬性映射到控件的屬性上:

 

textBoxName.DataBindings.Add ( "Text",myDataValue, "Name" );

 

上面的代碼就把textBoxName控件的“Text”屬性上綁定了MyDataValue對象的"Name"屬性。在內部有兩個對象,綁定管理(BindingManager)和流通管理(CurrencyManager), 實現了在控件與數據源之間的傳輸實現。你很可能已經見過爲種結構的例子,特別是在DataSet和DataGrid之間的。你也很可能已經做過數據綁定的例子。你很可能只在表面上簡單的使用過從數據綁定上得到的功能。你可以通過高效的數據綁定避免寫重複的代碼。

關於數據綁定的完整處理方案可能至少要花上一本書來說明,要不就是兩本。Windows應用程序和Web應用程序同時都支持數據綁定。比寫一個完整的數據綁定論述要強的是,我確實想讓你記住數據綁定的核心好處。首先,使用數據綁定比你自己寫代碼要簡單得多。其次,你應該在對文字元素通過屬性來顯示時,儘可能的使用它,它可以很好的綁定。第三,在Windows窗體中,可以同步的對綁定在多控件上的數據,進行相關數據源的檢測。

例如,假設只要在數據不合法時,要求將文字顯示爲紅色,你可能會寫這樣的代碼:

 

if ( src.TextIsInvalid )
{
textBox1.ForeColor 
= Color.Red;
}
 else
{
textBox1.ForeColor 
= Color.Black;
}

 

這很好,但只要在文字源發生改變時,你要隨時調用這段代碼。這可能是在用戶編輯了文字,或者是在底層的數據源發生改變時。這裏有太多的事件要處理了,而且很多地方你可能會錯過。但,使用數據綁定時,在src對象上添加一個屬性,返回恰當的前景顏色就行了。

 

這很好,但只要在文字源發生改變時,你要隨時調用這段代碼。這可能是在用戶編輯了文字,或者是在底層的數據源發生改變時。這裏有太多的事件要處理了,而且很多地方你可能會錯過。但,使用數據綁定時,在src對象上添加一個屬性,返回恰當的前景顏色就行了。

 

private Color _clr = Color.Black;
public Color ForegroundColor
{
get
{
    
return _clr;
}

}


private string _txtToDisplay;
public string Text
{
get
{
    
return _txtToDisplay;
}

set
{
    _txtToDisplay 
= value;
    UpdateDisplayColor( IsTextValid( ) );
}

}


private void UpdateDisplayColor( bool bValid )
{
_clr 
= ( bValid ) ? Color.Black : Color.Red;
}


/**////簡單的添加綁定到文本框裏就行了:

textBox1.DataBindings.Add (
"ForeColor",
src, 
"ForegroundColor");

 

當數據綁定配置好以後,textBox1會根據內部源對象的值,用正確的顏色來繪製文本。這樣,你就已經大大減少了從源數據到控件的數據來回傳輸。不再須要對不同地方顯示不同顏色來處理很多事件了。你的數據源對象保持對屬性的正確顯示進行跟蹤,而表單控件對數據綁定進行控制。

通過這個例子,我演示了Windows表單的數據綁定,同樣的在web應用程序中也是一樣的原則:你可以很好的綁定數據源的屬性到web控件的屬性上:

 

<asp:TextBox id=TextBox1 runat="server"
Text
="<%# src.Text %>"
ForeColor
="<%# src.ForegroundColor %>">

 

這就是說,當你創建一個應用程序在UI上顯示的類型時,你應該添加一些必須的屬性來創建和更新你的UI,以便用戶在必要時使用。

當你的對象不支持你要的屬性時怎麼辦呢?那就把它封裝成你想要的。看這樣的數據結構:

 

public struct FinancialResults
{
public decimal Revenue
{
    
get return _revenue; }
}


public int NumberOfSales
{
    
get return _numSales; }
}


public decimal Costs
{
    
get return _cost;}
}


public decimal Profit
{
    
get return _revenue - _cost; }
}

}

 

要求你在一個表單上以特殊的格式信息來顯示這些,如果收益爲負,你必須以紅色來顯示收益。如果薪水小於100,你應該用粗體顯示。如果開銷在10千(1萬)以上,你也應該用粗體顯示。創建FinancialResults結構的開發者沒有添加UI功能到這個結構上。這很可能是正確的選擇,FinancialResults應該限制它的功能,只用於存儲實際的值。你可以創建一個新類型,包含UI格式化屬性,以及在FinancialResults結構中的原始的存儲屬性:

 

public struct FinancialDisplayResults
{
private FinancialResults _results;
public FinancialResults Results
{
    
get return _results; }
}


public Color ProfitForegroundColor
{
    
get
    
{
      
return ( _results.Profit >= 0 ) ?
        Color.Black : Color.Red;
    }

}


// other formatting options elided
}


//這樣,你就創建了一個簡單的數據結構來幫助你所包含的數據結構來進行數據綁定:

// Use the same datasource. That creates one Binding Manager
textBox1.DataBindings.Add ("Text", src, "Results.Profit");
textBox1.DataBindings.Add (
"ForeColor",src,”ProfitForegroundColor");

 

我已經創建了一個只讀的屬性,用於訪問核心的財政數據結構。這種構造在你試圖支持對數據的讀寫操作時不能工作,FinancialResults結構是值類型,這就是說獲取訪問器不提供對存儲空間的訪問,它只是返回一個拷貝。這樣的方式很樂意返回一個拷貝,而這樣的拷貝並不能在數據綁定中進行修改。然而,如果你試圖對數據進行編輯時,FinancialResults類應該是一個類,而不是一個結構(參見原則6)。做爲一個引用類型,你的獲取訪問器返回一個內部存儲的引用,而且可以被用戶編輯。內部的結構應該須要對存儲的數據發生改變時做出響應。FinancialResults應該觸發事件來告訴其它代碼這一狀態的改變。

有一個很重要的事情要記住:把數據源用在同一表單中的所有相關控件上。使用DataMember屬性來區別每個控件顯示的屬性。你可以像這樣寫綁定過程:

 

// Bad practice: creates two binding managers
textBox1.DataBindings.Add ("Text",src.Results, "Profit");
textBox1.DataBindings.Add ("ForeColor",src,“rofitForegroundColor");


 

這會創建兩個綁定管理者,一個爲src對象,另一個爲src.Results對象。每個數據源由不同的綁定管理者控制,如果你想讓綁定管理者在數據源發生改變時,更新所有的屬性,你須要確保數據源是一致的。

你幾乎可以在所有的Windows控件和web控件上使用數據綁定。在控件裏顯示的值,字體,只讀狀態,甚至是控件控件的位置,都可以成爲綁定操作的對象。我的建議是創建類或者結構,包含一些用戶要求的,以某種樣式顯示的數據。這些數據就是用於更新控件。

另外,在簡單控件中,數據綁定經常出現在DataSet和DataGrids中。這非常有用,你把DataGrid綁定到DataSet上,然後DataSet中所有的值就顯示了。如果你的DataSet有多個表,你甚至還可以在多個表中間進行導航。這不是很好嗎?

好了,下面的問題就是如果你的數據集不包含你想顯示的字段時該怎麼辦。這時,你必須添加一個列到DataSet中,這一列計算一些UI中必須的值。如果值可以用SQL表達式計算,那麼DataSet可以爲你完成。下面的代碼就添加了一個列到Employees 數據表中,用於顯示格式化了名字:

DataTable dt = data.Tables[ "Employees" ];
dt.Columns.Add( "EmployeeName",
typeof( string ),
  "lastname + ', ' + firstname");



通過添加列到DataSet中,你可以添加這些列到DataGrid上。你所創建的對象層,是在數據存儲對象的最項層上,用於創建數據表現層給你的用戶。

 

 

到目前爲止,這一原則裏所使用的都是string類型,.net框架可以處理字符到數字的轉化:它試圖轉化用戶的輸入到恰當的類型。如果失敗,原始的值會恢復。這是可以工作的,但用戶完全沒的反饋信息,他們的輸出被安靜的忽略了。你可以通過處理綁定過程中的轉化事件來添加反饋信息。這一事件在綁定管理者從控件上更新值到數據源時發生。ParseEventArgs包含了用戶輸入的文字 ,以及它所期望被轉化的類型。你可以捕獲這一事件,其後完成你自己的通知,也可以修改數據並且用你自己的值來更新數據:

 
private void Form1_Parse( object sender, ConvertEventArgs e )
{
try {
    Convert.ToInt32 ( e.Value );
}
 catch
{
    MessageBox.Show (
    
string.Format( "{0} is not an integer",
      e.Value.ToString( ) ) );
    e.Value 
= 0;
}

}



你可能還要處理Format事件,這一個HOOK,可以在數據從數據源到控件時格式化數據。你可以修改ConvertEventArgs的Value字段來格式化必須顯示的字符串。

.Net提供了通用的框架,可以讓你支持數據綁定。你的工作就是爲你的應用程序和數據提供一些特殊的事件句柄。Windows表單和Web表單以及子系統都包含了豐富的數據綁定功能。框架庫已經包含了所有你須要的工具,因此,你的UI代碼應該真實的描述數據源和要顯示的屬性,以及在把這些元素存儲到數據源時須要遵守的規則。你應該集中精力創建數據類型,用於描述顯示的參數,然後Winform以及Webform的數據綁定完成其它的。不應該在把數據從用戶控件到數據源之間進行傳輸時寫相關的代碼(譯註:指用數據綁定,而不用其它的方法)。不管怎樣,數據必須從你的業務對象關聯到UI控件上與用戶進行交互。通過創建類型層以及使用數據綁定的概念,你就可以少寫很多代碼。.Net框架已經 同時在Windows和Web應用程序中爲你處理了傳輸的工作。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章