asp.net控件開發基礎(21)

作者: Clingingboy  來源: 博客園  發布時間: 2010-10-02 19:49  閱讀: 872 次  推薦: 0   原文鏈接   [收藏]  

  上篇介紹了在asp.net2.0版本下面如何簡單的定義數據綁定控件。雖然DataBoundControl為我們提供了便利,我們以后可以從此類開始編寫數據綁定控件。但是在2.0版本未到來之前,你已經為自己訂制了一些數據綁定控件,既然2.0版本已經提供了數據源控件,你是否有想法,讓你原有的控件也升級到同時支持通過設置DataSource屬性和數據源控件來獲取數據源,這樣以后我們就可以省省工作了。這次我們就來討論這個話題,讓舊版本的數據綁定控件支持數據源控件。

  一.準備升級數據綁定控件

  即使asp.net1.1版本的一些控件也都已經支持數據源控件了,如Repeater,BaseDataList等.但本身這些對象并不是從BaseDataBoundControl和DataBoundControl等類繼承下來的,如Repeater其是從Control下繼承的一個模板控件,其并不需要這么多從WebControl繼承下來的屬性,如果你想讓它支持數據源控件,你首先會想到改變控件基類,從DataBoundControl開始,這是一個好想法,但可能有些情況下并不允許這么做。上次說到了BaseDataList和DataBoundControl,BaseDataList也支持數據源控件了,所以我認為從此類繼承是完全沒有問題的。另外的做法就是在不改變原有控件基類的情況下,你還是需要老老實實給原控件添加一些代碼支持數據源控件。那么就開始吧。

  二.具體實現

  本次例子跟上篇相同,相同地方就略過了

  1.定義基本成員

  整個控件的實現方式跟DataBoundControl實現方式很相似,我們可以看看MSDN中,BaseDataList等基類添加了哪些元素,然后模仿著實現.如果對BaseDataBoundControl和DataBoundControl這兩個類成員了解的話,你將對下面成員屬性很熟悉,添加這些基本成員

  (1)

 
/// <summary>
/// 該值指示控件是否已經初始化
/// </summary>
protected bool Initialized
{

get
{
return initialized;
}
}

public string DataMember
{

get
{
object member = ViewState["DataMember"];
if (member == null)
return string.Empty;
else
return (string)member;
}

set
{
ViewState[
"DataMember"] = value;
this.OnDataPropertyChanged();
}
}

/// <summary>
/// 為數據綁定控件提供數據源
/// </summary>
public IEnumerable DataSource
{

get
{
return dataSource;
}

set
{
if ((value is IEnumerable) || (value is IListSource) || (value == null))
dataSource
= value;
else
throw new Exception("錯誤的數據源類型");
OnDataPropertyChanged();
}
}


/// <summary>
/// 數據源控件的 ID 屬性
/// </summary>
[DefaultValue(""), IDReferenceProperty(typeof(DataSourceControl))]
public virtual string DataSourceID
{

get
{
object dataSourceID = ViewState["DataSourceID"];
if (dataSourceID != null)
{

return (string)dataSourceID;
}

return string.Empty;
}

set
{
this.ViewState["DataSourceID"] = value;
this.OnDataPropertyChanged();
}
}


/// <summary>
/// 獲取是否設置 DataSourceID 屬性的值
/// </summary>
protected bool IsBoundUsingDataSourceID
{

get
{
return (DataSourceID.Length > 0);
}
}


/// <summary>
/// 是否需要綁定到其指定的數據源
/// </summary>
protected bool RequiresDataBinding
{

get
{
return requiresDataBinding;
}

set
{
requiresDataBinding
= value;
}
}


/// <summary>
/// 用于檢索數據的 DataSourceSelectArguments 對象。默認為 Empty 值
/// </summary>
protected DataSourceSelectArguments SelectArguments
{

get
{
if (selectArguments == null)
{
selectArguments
= CreateDataSourceSelectArguments();
}

return selectArguments;
}
}
(2)上面幾個屬性涉及到幾個方法
 
/// <summary>
/// 創建空的 DataSourceSelectArguments 對象
/// </summary>
/// <returns></returns>
protected virtual DataSourceSelectArguments CreateDataSourceSelectArguments()
{

return DataSourceSelectArguments.Empty;
}


/// <summary>
/// 如果設置了 DataSourceID 屬性且數據綁定控件標記為需要綁定,則調用 DataBind 方法
/// OnPreRender中調用
/// </summary>
protected void EnsureDataBound()
{

if (RequiresDataBinding && (DataSourceID.Length > 0))
{
DataBind();
}
}

/// <summary>
/// 在某一基數據源標識屬性更改后,將數據綁定控件重新綁定到其數據
/// </summary>
protected virtual void OnDataPropertyChanged()
{

if (initialized)
{
RequiresDataBinding
= true;
}
currentViewValid
= false;
}

  上面的幾個屬性和方法可以一起來看看了,在更改數據源標識時都會調用OnDataPropertyChanged方法,然后到了EnsureDataBound方法(此方法在OnPreRender方法中調用)在使用數據源控件情況下自動調用DataBind方法。另外Initialized屬性會在控件初始化時設置。

  2.獲取與數據綁定控件關聯的IDataSource 接口
數據源控件實現了IDataSource接口,此接口定義了數據源最基本的元素,數據綁定控件要根據DataSourceID屬性從容器中獲取與其關聯的 IDataSource 接口。如下實現

 
// 從容器中獲取DataControl
private Control FindControl(Control control, string controlID)
{
Control namingContainer
= control;
Control dataControl
= null;
if (control != control.Page)
{

while ((dataControl == null) && (namingContainer != control.Page))
{
namingContainer
= namingContainer.NamingContainer;
if (namingContainer == null)
{

throw new HttpException("DataBoundControlHelper_NoNamingContainer");
}
dataControl
= namingContainer.FindControl(controlID);
}

return dataControl;
}

return control.FindControl(controlID);
}


/// <summary>
/// 檢索與數據綁定控件關聯的 IDataSource 接口
/// </summary>
/// <returns></returns>
protected virtual IDataSource GetDataSource()
{

if (this.currentDataSource != null)
{

return currentDataSource;
}


//獲取數據源控件
IDataSource source = null;
string controlID = DataSourceID;
if (controlID.Length != 0)
{
Control control
= FindControl(this, controlID);
source
= control as IDataSource;
}

return source;
}

  3.獲取數據源視圖

  第二步的實現是為此服務的

 
private DataSourceView ConnectToDataSourceView()
{


if (!currentViewValid || base.DesignMode)
{


if ((currentView != null) && currentViewIsFromDataSourceID)
{
currentView.DataSourceViewChanged
-= new EventHandler(this.OnDataSourceViewChanged);
}


this.currentDataSource = GetDataSource();

//從DataSource獲取數據源
if (this.currentDataSource == null)
{

this.currentDataSource = new ReadOnlyDataSource(DataSource, DataMember);
}

DataSourceView view
= this.currentDataSource.GetView(DataMember);
currentViewIsFromDataSourceID
= IsBoundUsingDataSourceID;
currentView
= view;

if ((currentView != null) && currentViewIsFromDataSourceID)
{
currentView.DataSourceViewChanged
+= new EventHandler(this.OnDataSourceViewChanged);
}
currentViewValid
= true;
}

return currentView;
}


/// <summary>
/// 獲取數據源視圖
/// </summary>
/// <returns></returns>
protected virtual DataSourceView GetData()
{

return ConnectToDataSourceView();
}

  請注意ConnectToDataSourceView方法,前后分別在移除和添加一個事件,將RequiresDataBinding屬性設置為true重新綁定,然后再看中間這段代碼

 
if (this.currentDataSource == null)
{

this.currentDataSource = new ReadOnlyDataSource(DataSource, DataMember);
}

  即當未使用數據源控件時,則就從ReadOnlyDataSource對象通過設置DataSource和DataMember屬性來獲取IDataSource 接口,然后才能獲取到數據源視圖.下面為ReadOnlyDataSource和ReadOnlyDataSourceView的簡單實現,在此不做解釋.下次再來講這個東西。

 

 
public class ReadOnlyDataSource : IDataSource
{


private string _dataMember;
private object _dataSource;
private static string[] ViewNames = new string[0];


event EventHandler IDataSource.DataSourceChanged
{
add
{
}
remove
{
}
}



public ReadOnlyDataSource(object dataSource, string dataMember)
{

this._dataSource = dataSource;
this._dataMember = dataMember;
}

DataSourceView IDataSource.GetView(
string viewName)
{
IDataSource source
= _dataSource as IDataSource;
if (source != null)
{

return source.GetView(viewName);
}

return new ReadOnlyDataSourceView(this, this._dataMember,DataSourceHelper.ResolveDataSource(this._dataSource, this._dataMember));
}

ICollection IDataSource.GetViewNames()
{

return ViewNames;
}

}


public class ReadOnlyDataSourceView : DataSourceView
{


private IEnumerable dataSource;

public ReadOnlyDataSourceView(ReadOnlyDataSource owner, string name, IEnumerable dataSource)
:
base(owner, name)
{

this.dataSource=dataSource ;
}


protected override IEnumerable ExecuteSelect(DataSourceSelectArguments arguments)
{
arguments.RaiseUnsupportedCapabilitiesError(
this);
return dataSource;
}

}

  4.獲取數據

  接著你便可以在DataBind方法中通過獲取到的數據源視圖異步獲取數據了,本來我們可以調用其ExecuteSelect方法的,可惜我們無法調用此方法,只好異步調用。接著的PerformDataBinding方法跟上篇實現一樣。不再列出。記得在DataBind方法將RequiresDataBinding 屬性設置為true

 
/// <summary>
/// 將數據源綁定到控件
/// </summary>
public override void DataBind()
{

if (!IsBoundUsingDataSourceID)
{
OnDataBinding(EventArgs.Empty);
}

GetData().Select(CreateDataSourceSelectArguments(),
OnDataSourceViewSelectCallback);
RequiresDataBinding
= false;
MarkAsDataBound();
}

private void OnDataSourceViewSelectCallback(IEnumerable retrievedData)
{

if (IsBoundUsingDataSourceID)
{
OnDataBinding(EventArgs.Empty);
}
PerformDataBinding(retrievedData);
}

  5.重寫控件生命周期事件

  其中在OnPreRender方法中調用了EnsureDataBound方法,其他方法的話可以發現在很多不同情況下將RequiresDataBinding和Initialized屬性設置為True.做了數據綁定的初始化工作。這里估計我也解釋不清楚,大家還是了解下控件的生命周期,了解其事件的使用,再理解吧.這里可以參考jessezhao的這篇翻譯

 
protected override void OnInit(EventArgs e)
{

base.OnInit(e);
if (this.Page != null)
{

this.Page.PreLoad += new EventHandler(this.OnPagePreLoad);
if (!base.IsViewStateEnabled && this.Page.IsPostBack)
{

this.RequiresDataBinding = true;
}
}
}


private void OnPagePreLoad(object sender, EventArgs e)
{
initialized
= true;
if (Page != null)
{
Page.PreLoad
-= new EventHandler(OnPagePreLoad);
if (!Page.IsPostBack)
{
RequiresDataBinding
= true;
}

if ((Page.IsPostBack && base.IsViewStateEnabled) && (ViewState["DataBound"] == null))
{
RequiresDataBinding
= true;
}
}
}


protected override void OnPreRender(EventArgs e)
{
EnsureDataBound();

base.OnPreRender(e);
}


protected override void OnLoad(EventArgs e)
{

this.initialized = true;
this.ConnectToDataSourceView();
if (this.Page != null && this.ViewState["DataBound"] == null)
{

if (!this.Page.IsPostBack)
{

this.RequiresDataBinding = true;
}

else if (base.IsViewStateEnabled)
{

this.RequiresDataBinding = true;
}
}

base.OnLoad(e);
}

  好了,基本代碼的編寫就完成了,接著你就可以通過設置DataSource屬性手動綁定的形式和設置DataSourceID屬性獲取數據源的形式獲取數據了。

  這篇可以供參考,如果真要這么做的話,幾乎每個原有的數據綁定控件都需要重復編寫上面這么多代碼。相比之下如DataBoundControl類和BaseDataList類都已經幫你完成了上面的工作,在有選擇的情況下,我們當然不愿意寫上面這么多的代碼。所以說上面的這堆代碼也只供你參考,能夠使用新的基類的話,盡量使用,如果真的需要這么做的話,你就需要這么去改你的數據綁定控件。

上一篇:asp.net控件開發基礎(20)

下一篇:asp.net控件開發基礎(22)
0
0
 
 
 

文章列表

全站熱搜
創作者介紹
創作者 大師兄 的頭像
大師兄

IT工程師數位筆記本

大師兄 發表在 痞客邦 留言(0) 人氣()