一起谈.NET技术,asp.net控件开发基础(21)
2011-09-01 23:47
876 查看
上篇介绍了在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)
(2)上面几个属性涉及到几个方法
上面的几个属性和方法可以一起来看看了,在更改数据源标识时都会调用OnDataPropertyChanged方法,然后到了EnsureDataBound方法(此方法在OnPreRender方法中调用)在使用数据源控件情况下自动调用DataBind方法。另外Initialized属性会在控件初始化时设置。
2.获取与数据绑定控件关联的IDataSource 接口
数据源控件实现了IDataSource接口,此接口定义了数据源最基本的元素,数据绑定控件要根据DataSourceID属性从容器中获取与其关联的 IDataSource 接口。如下实现
3.获取数据源视图
第二步的实现是为此服务的
请注意ConnectToDataSourceView方法,前后分别在移除和添加一个事件,将RequiresDataBinding属性设置为true重新绑定,然后再看中间这段代码
即当未使用数据源控件时,则就从ReadOnlyDataSource对象通过设置DataSource和DataMember属性来获取IDataSource 接口,然后才能获取到数据源视图.下面为ReadOnlyDataSource和ReadOnlyDataSourceView的简单实现,在此不做解释.下次再来讲这个东西。
4.获取数据
接着你便可以在DataBind方法中通过获取到的数据源视图异步获取数据了,本来我们可以调用其ExecuteSelect方法的,可惜我们无法调用此方法,只好异步调用。接着的PerformDataBinding方法跟上篇实现一样。不再列出。记得在DataBind方法将RequiresDataBinding 属性设置为true
5.重写控件生命周期事件
其中在OnPreRender方法中调用了EnsureDataBound方法,其他方法的话可以发现在很多不同情况下将RequiresDataBinding和Initialized属性设置为True.做了数据绑定的初始化工作。这里估计我也解释不清楚,大家还是了解下控件的生命周期,了解其事件的使用,再理解吧.这里可以参考jessezhao的这篇翻译。
好了,基本代码的编写就完成了,接着你就可以通过设置DataSource属性手动绑定的形式和设置DataSourceID属性获取数据源的形式获取数据了。
这篇可以供参考,如果真要这么做的话,几乎每个原有的数据绑定控件都需要重复编写上面这么多代码。相比之下如DataBoundControl类和BaseDataList类都已经帮你完成了上面的工作,在有选择的情况下,我们当然不愿意写上面这么多的代码。所以说上面的这堆代码也只供你参考,能够使用新的基类的话,尽量使用,如果真的需要这么做的话,你就需要这么去改你的数据绑定控件。
一.准备升级数据绑定控件
即使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类都已经帮你完成了上面的工作,在有选择的情况下,我们当然不愿意写上面这么多的代码。所以说上面的这堆代码也只供你参考,能够使用新的基类的话,尽量使用,如果真的需要这么做的话,你就需要这么去改你的数据绑定控件。
相关文章推荐
- 一起谈.NET技术,asp.net控件开发基础(13)
- 一起谈.NET技术,asp.net控件开发基础(14)
- 一起谈.NET技术,asp.net控件开发基础(20)
- 一起谈.NET技术,asp.net控件开发基础(2)
- 一起谈.NET技术,asp.net控件开发基础(16)
- 一起谈.NET技术,asp.net控件开发基础(19)
- 一起谈.NET技术,asp.net控件开发基础(12)
- 一起谈.NET技术,asp.net控件开发基础(11)
- 一起谈.NET技术,asp.net控件开发基础(1)
- 一起谈.NET技术,asp.net控件开发基础(10)
- 一起谈.NET技术,asp.net控件开发基础(23)
- 一起谈.NET技术,asp.net控件开发基础(9)
- 一起谈.NET技术,asp.net控件开发基础(8)
- 一起谈.NET技术,asp.net控件开发基础(7)
- 一起谈.NET技术,asp.net控件开发基础(6)
- 一起谈.NET技术,asp.net控件开发基础(17)
- 一起谈.NET技术,asp.net控件开发基础(5)
- 一起谈.NET技术,asp.net控件开发基础(22)
- 一起谈.NET技术,asp.net控件开发基础(4)
- 一起谈.NET技术,asp.net控件开发基础(15)