zoukankan      html  css  js  c++  java
  • 一起谈.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)
  • 相关阅读:
    HCIA_R&S-学习_Day05(PPPoE、DHCP、ACL、NAT)
    HCIA_R&S-学习_Day04(链路状态协议OSPF & PPP)
    HCIA_R&S-学习_Day03(路由协议基础与实现)
    【详谈 Delta Lake 】系列技术专题 之 湖仓一体( Lakehouse )
    工作7年,我的10条经验总结
    Hologres揭秘:优化COPY,批量导入性能提升5倍+
    谈谈JVM内部锁升级过程
    如何帮用户管好云账本?阿里云数据库助力收钱吧 | 甲子光年
    重磅 | 数据库自治服务DAS论文入选全球顶会SIGMOD,领航“数据库自动驾驶”新时代
    同程旅行基于 RocketMQ 高可用架构实践
  • 原文地址:https://www.cnblogs.com/waw/p/2162815.html
Copyright © 2011-2022 走看看