一起谈.NET技术,asp.net控件开发基础(21)

  上篇介绍了在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)

时间: 2024-09-20 22:30:49

一起谈.NET技术,asp.net控件开发基础(21)的相关文章

ASP.NET控件开发基础(21)

上篇介绍了在asp.net2.0版本下面如何简单的定义数据绑定控件.虽然DataBoundControl为我们提供了便利,我们以后可以从此类开始编写数据绑定控件.但是在2.0版本未到来之前,你已经为自己订制了一些数据绑定控件,既然2.0版本已经提供了数据源控件,你是否有想法,让你原有的控件也升级到同时支持通过设置DataSource属性和数据源控件来获取数据源,这样以后我们就可以省省工作了.这次我们就来讨论这个话题,让旧版本的数据绑定控件支持数据源控件. 一.准备升级数据绑定控件 即使asp.n

一起谈.NET技术,asp.net控件开发基础(22)

上两篇讨论了如何定义结合数据源控件的数据绑定控件.这次我们一起来看下数据源控件是如何实现的.asp.net2.0已经为我们提供了很多数据源控件,相信大家都用过了,也希望大家对其有所熟悉.关于它能做什么就不说了.下面我们也一起来看看,如何简单的实现. 一.你必须了解的 1.关于数据源控件(DataSourceControl) 虽然表面看来,给数据绑定控件指定DataSourceID属性,数据源控件帮你做了一切工作,其实不然,数据源控件只负责收集与数据交互的相关信息,如:SqlDataSource的

一起谈.NET技术,asp.net控件开发基础(20)

上面我们讨论了数据绑定控件的做法,但都未涉及到asp.net2.0中数据源控件的用法,让用惯了数据源控件的人可能感觉不适应.这次我们就开始讨论在asp.net2.0中,我们该如何重新定义数据绑定控件.我一直在想,是先讨论数据源控件呢,还是先讨论数据绑定控件,两者是密不可分的.在看下文之前,我想大家应该对asp.net2.0中的数据源控件使用的简易性非常熟悉了.记得园子的开源项目NBear也为大家提供了数据源控件.个人认为数据源控件和数据绑定控件之间存在着一些约定关系,学习还当从易到难,这里就假定

一起谈.NET技术,asp.net控件开发基础(15)

继续我们的话题吧.自定义控件.如果你还不熟悉自定义控件开发的话,还请看看我以前写了几篇,希望对你有帮助 1.1何处继承 自定义控件一般从以下几个基类(此处不包含数据控件) 一.Control类(所有服务器控件的基类,算是比较底层的类,如果控件功能比较简单,要求不多,可直接继承此类.) 二.WebControl类(标准控件的基类,继承此类,你可以继承其丰富的公共属性,若标准控件中的控件没有你需要的控件,你可以继承此类) 三.CompositeControl 类(2.0新增的类,此类继承自WebCo

一起谈.NET技术,asp.net控件开发基础(13)

1.减轻服务器压力,增加用户体验 服务器功能是强大的,客户端脚本一点也不弱,现在的ajax技术和Atlas技术就是最好的证明,我们总是期待UI有一个好的效果,flash动画给我们带来了很酷的效果,我们至少也可以为我们的服务器控件添加客户端脚本,一方面减少了服务器端的回传,一方面又能为控件提供非常酷的效果.我想我们都很喜欢ATLAS里面很多很酷的控件吧,而且无刷新,服务器控件与客户端脚本交互使用,那会服务器控件变的更加完美. 经过上面的废话,下面我们进入正题 2.简单为服务器控件添加客户端脚本 我

一起谈.NET技术,asp.net控件开发基础(8)

有一些复合控件直接把按钮触发事件所需的事情封装好,另外一种则是自定义事件,更具灵活性,当然这是根据需要设计的.以下会以例子来说明的.下面我们假设我们控件中有两个按钮.以下不列出所有代码,具体可在文章最后下载代码. (1) 直接实现按钮事件 在控件中(以下代码并非实现复合控件)直接实现事件则无需自定义事件,如下代码(如果对数据回传有些不熟悉的话,可先看第三篇,希望对你有帮助) 示例一(只列出局部代码,具体可在文章最后下载代码) void IPostBackEventHandler.RaisePos

一起谈.NET技术,asp.net控件开发基础(2)

或许大家还对为何要重写Render方法存有疑惑,希望大家看看我举的例子,能够明白Render方法和其他两个方法的作用,然后真正明白为何一般情况下只须重写Render方法.我们知道我们每次编写控件时,都需要重写Render方法,我们发现在Control类中很多方法可以重写,但我们没有去重写他们,我们需要遵循一个原则,在需要重载的时候再去重写他们 我们还是先来看看与Render方法相关的两个方法 //RenderControl方法的基本实现 public void RenderControl(Htm

一起谈.NET技术,asp.net控件开发基础(18)

本篇继续上篇的讨论,可能大家已经在使用asp.net2.0了,DataSource属性不再使用,而是跟数据源控件搭配使用.现在讨论的绑定技术都是基于1.1版本,先熟悉一下,本质上是一样的,这样一步步的学习.对以后绝对有帮助.因为当你使用数据源控件,只需要设置一个DataSourceID,方便的同时你是否知道数据源控件帮你做了什么事情,如果你想觉的够用了,可以不用了解,但我相信你一定会有需求.上篇最后说过了,讨论还刚刚开始,我们大致把核心的方法都写出来了.下面我们继续. 一.控件对比 我们可以使用

一起谈.NET技术,asp.net控件开发基础(17)

本篇将开始介绍如自定义数据绑定控件,这里感谢很多人的支持,有你们的支持很高兴.这里首先需要大家熟悉asp.net模板控件的使用,还有自定义模板控件.因为数据绑定控件多是基于模板控件的. 一.回顾 如果你使用过asp.net内置的数据控件(如DataList,Repeater),你一定会这么做 1.设置数据源 DataSource属性 2.调用数据绑定  DataBind方法 3.在控件的不同模板内使用绑定语法显示数据 这三步应该是必须要做的 其他更多的 你可能需要对绑定的数据进行统一的一些操作(