為了在我們自己定義的集合上也實現這樣 的概念,我們很要做點工作。你要修改你的AddressRecord 結構來支持兩個新的 接口,IEditableObject 和IDataErrorInfo。IEditableObject 為你的類型提供 了對事務的支持。IDataErrorInfo 提供了常規的錯誤處理。為了支持事務,你 必須修改你的數據存儲來提供你自己的回滾功能。你可能在多個列上有錯誤,因 此你的存儲必須包含一個包含了每個列的錯誤集合。這是一個為AddressRecord 做的更新的列表:
public class AddressRecord : IEditableObject, IDataErrorInfo
{
private struct AddressRecordData
{
public string street;
public string city;
public string state;
public string zip;
}
private AddressRecordData permanentRecord;
private AddressRecordData tempRecord;
private bool _inEdit = false;
private IList _container;
private Hashtable errors = new Hashtable();
public AddressRecord( AddressList container )
{
_container = container;
}
public string Street
{
get
{
return ( _inEdit ) ? tempRecord.street :
permanentRecord.street;
}
set
{
if ( value.Length == 0 )
errors[ "Street" ] = "Street cannot be empty";
else
{
errors.Remove( "Street" );
}
if ( _inEdit )
tempRecord.street = value;
else
{
permanentRecord.street = value;
int index = _container.IndexOf( this );
_container[ index ] = this;
}
}
}
public string City
{
get
{
return ( _inEdit ) ? tempRecord.city :
permanentRecord.city;
}
set
{
if ( value.Length == 0 )
errors[ "City" ] = "City cannot be empty";
else
{
errors.Remove( "City" );
}
if ( _inEdit )
tempRecord.city = value;
else
{
permanentRecord.city = value;
int index = _container.IndexOf( this );
_container[ index ] = this;
}
}
}
public string State
{
get
{
return ( _inEdit ) ? tempRecord.state :
permanentRecord.state;
}
set
{
if ( value.Length == 0 )
errors[ "State" ] = "City cannot be empty";
else
{
errors.Remove( "State" );
}
if ( _inEdit )
tempRecord.state = value;
else
{
permanentRecord.state = value;
int index = _container.IndexOf( this );
_container[ index ] = this;
}
}
}
public string Zip
{
get
{
return ( _inEdit ) ? tempRecord.zip :
permanentRecord.zip;
}
set
{
if ( value.Length == 0 )
errors["Zip"] = "Zip cannot be empty";
else
{
errors.Remove ( "Zip" );
}
if ( _inEdit )
tempRecord.zip = value;
else
{
permanentRecord.zip = value;
int index = _container.IndexOf( this );
_container[ index ] = this;
}
}
}
public void BeginEdit( )
{
if ( ( ! _inEdit ) && ( errors.Count == 0 ) )
tempRecord = permanentRecord;
_inEdit = true;
}
public void EndEdit( )
{
// Can't end editing if there are errors:
if ( errors.Count > 0 )
return;
if ( _inEdit )
permanentRecord = tempRecord;
_inEdit = false;
}
public void CancelEdit( )
{
errors.Clear( );
_inEdit = false;
}
public string this[string columnName]
{
get
{
string val = errors[ columnName ] as string;
if ( val != null )
return val;
else
return null;
}
}
public string Error
{
get
{
if ( errors.Count > 0 )
{
System.Text.StringBuilder errString = new
System.Text.StringBuilder();
foreach ( string s in errors.Keys )
{
errString.Append( s );
errString.Append( ", " );
}
errString.Append( "Have errors" );
return errString.ToString( );
}
else
return "";
}
}
}
花了幾頁的代碼來支持一些已經在DataSet裡實現的了的功能 。實際上,這還不能像DataSet那樣恰當的工作。例如,交互式的添加一個新記 錄到集合中,以及支持事務所要求的BeginEdit, CancelEdit, 和EndEdit等。 你要在CancelEdit 調用時檢測一個新的對象而不是一個已經修改了的對象。 CancelEdit 必須從集合上移除這個新的對象,該對象應該是上次調用BeginEdit 時創建的。對於AddressRecord 來說,還有很多修改要完成,而且一對事件還要 添加到AddressList 類上。
最後,就是這個IBindingList接口。這個接 口至少包含了20個方法和屬性,用於控件查詢列表上的功能描述。你必須為只讀 列表實現IBindingList 或者交互排序,或者支持搜索。在你取得內容之前就陷 於層次關系和導航關系中了。我也不准備為上面所有的代碼添加任何例子了。
幾頁過後,再問問你自己,還准備創建你自己的特殊集合嗎?或者你想 使用一個DataSet嗎?除非你的集合是一個基於某些算法,對性能要求嚴格的集 合,或者必須有輕便的格式,就要使用自己的DataSet,特別是類型化的DataSet 。這將花去你大量的時間,是的,你可以爭辯說DataSet並不是一個基於面向對 象設計的最好的例子。類型化的DataSet甚至會破壞更多的規則。但,使用它所 產生的代碼開發效率,比起自己手寫更優美的代碼所花的時間,這只是其中一小 部份。
返回教程目錄