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

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

  上篇介绍了在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)
目录
相关文章
|
5月前
|
监控 Cloud Native 测试技术
.NET技术深度解析:现代企业级开发指南
每日激励:“不要一直责怪过去的自己,他曾经站在雾里也很迷茫”。我是蒋星熠Jaxonic,一名在代码宇宙中探索的极客旅人。从.NET Framework到.NET 8,我深耕跨平台、高性能、云原生开发,践行领域驱动设计与微服务架构,用代码书写技术诗篇。分享架构演进、性能优化与AI融合前沿,助力开发者在二进制星河中逐光前行。关注我,共探技术无限可能!
.NET技术深度解析:现代企业级开发指南
|
11月前
|
SQL 小程序 API
如何运用C#.NET技术快速开发一套掌上医院系统?
本方案基于C#.NET技术快速构建掌上医院系统,结合模块化开发理念与医院信息化需求。核心功能涵盖用户端的预约挂号、在线问诊、报告查询等,以及管理端的排班管理和数据统计。采用.NET Core Web API与uni-app实现前后端分离,支持跨平台小程序开发。数据库选用SQL Server 2012,并通过读写分离与索引优化提升性能。部署方案包括Windows Server与负载均衡设计,确保高可用性。同时针对API差异、数据库老化及高并发等问题制定应对措施,保障系统稳定运行。推荐使用Postman、Redgate等工具辅助开发,提升效率与质量。
461 0
|
开发框架 算法 .NET
C#/.NET/.NET Core技术前沿周刊 | 第 15 期(2024年11.25-11.30)
C#/.NET/.NET Core技术前沿周刊 | 第 15 期(2024年11.25-11.30)
254 6
|
开发框架 Cloud Native .NET
C#/.NET/.NET Core技术前沿周刊 | 第 16 期(2024年12.01-12.08)
C#/.NET/.NET Core技术前沿周刊 | 第 16 期(2024年12.01-12.08)
275 6
|
机器学习/深度学习 人工智能 Cloud Native
在数字化时代,.NET 技术凭借其跨平台兼容性、丰富的类库和工具集以及卓越的性能与效率,成为软件开发的重要平台
在数字化时代,.NET 技术凭借其跨平台兼容性、丰富的类库和工具集以及卓越的性能与效率,成为软件开发的重要平台。本文深入解析 .NET 的核心优势,探讨其在企业级应用、Web 开发及移动应用等领域的应用案例,并展望未来在人工智能、云原生等方面的发展趋势。
344 3
|
监控 前端开发 API
一款基于 .NET MVC 框架开发、功能全面的MES系统
一款基于 .NET MVC 框架开发、功能全面的MES系统
543 5
|
开发框架 前端开发 .NET
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
575 0
|
开发框架 前端开发 JavaScript
ASP.NET MVC 教程
ASP.NET 是一个使用 HTML、CSS、JavaScript 和服务器脚本创建网页和网站的开发框架。
327 7
|
存储 开发框架 前端开发
ASP.NET MVC 迅速集成 SignalR
ASP.NET MVC 迅速集成 SignalR
334 0
|
开发框架 前端开发 .NET
ASP.NET MVC WebApi 接口返回 JOSN 日期格式化 date format
ASP.NET MVC WebApi 接口返回 JOSN 日期格式化 date format
274 0