示例代码

上篇介绍了在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接口,此接口定义了数据源最基本的元素,数据绑定控件要根据DataSourceID属性从容器中获取与其关联的 接口。如下实现

     // 从容器中获取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类都已经帮你完成了上面的工作,在有选择的情况下,我们当然不愿意写上面这么多的代码。所以说上面的这堆代码也只供你参考,能够使用新的基类的话,尽量使用,如果真的需要这么做的话,你就需要这么去改你的数据绑定控件。

这篇可能讲的不是很详细,大家如果真的有必要这么做的话,可以仔细看看。不足之处还请大家纠正^_^.
晚了,睡觉去了。

转载于:https://www.cnblogs.com/hunterzou/archive/2008/12/19/1358575.html

asp.net控件开发基础(21)相关推荐

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

    上面我们讨论了数据绑定控件的做法,但都未涉及到asp.net2.0中数据源控件的用法,让用惯了数据源控件的人可能感觉不适应.这次我们就开始讨论在asp.net2.0中,我们该如何重新定义数据绑定控件. ...

  2. asp.net控件开发基础系列

    本系列文章示例源码下载.各位如遇问题,请多查msdn,多利用网络.本人可能没时间一一回复,谢谢你们的支持,希望看到此文的人都能学好控件开发 http://www.cnblogs.com/Clingin ...

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

    写第五篇的时候,我一步步的加上元数据(特性),使得设计时效果更加好,如对复杂属性应用以下特性,使属性浏览器支持扩展/折叠效果,使你更加容易编辑子属性,但接着我又遇到了问题,所以必须去解决 1.认识默认 ...

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

    本篇继续上篇的讨论,可能大家已经在使用asp.net2.0了,DataSource属性不再使用,而是跟数据源控件搭配使用.现在讨论的绑定技术都是基于1.1版本,先熟悉一下,本质上是一样的,这样一步步的 ...

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

    本篇将开始介绍如自定义数据绑定控件,这里感谢很多人的支持,有你们的支持很高兴.这里首先需要大家熟悉asp.net模板控件的使用,还有自定义模板控件.因为数据绑定控件多是基于模板控件的. 一.回顾 如果 ...

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

    1.减轻服务器压力,增加用户体验 服务器功能是强大的,客户端脚本一点也不弱,现在的ajax技术和Atlas技术就是最好的证明,我们总是期待UI有一个好的效果,flash动画给我们带来了很酷的效果,我们 ...

  7. ASP.NET控件开发基础5

    上一篇简单的讲了从WebControl继承的控件(好象我讲的都是简单的,嘿嘿).本次讲的更简单,主题是是属性,只当分享经验,希望对大家有帮助 我们根据属性的不同表现形式,把其区分为简单属性和复杂属性 ...

  8. asp.net控件开发基础十四

    http://www.cnblogs.com/Clingingboy/archive/2006/09/29/514722.html      上一篇讨论了为服务器控件添加客户端功能,这一篇我们所要讲的 ...

  9. asp.net控件开发基础(20)

    示例代码         上面我们讨论了数据绑定控件的做法,但都未涉及到asp.net2.0中数据源控件的用法,让用惯了数据源控件的人可能感觉不适应.这次我们就开始讨论在asp.net2.0中,我们该 ...

最新文章

  1. 数据库分析函数 MySQL_MySql数据库索引分析explain函数的使用
  2. python环境安装opencv,Python环境搭建之OpenCV的步骤方法
  3. Ubuntu下 ssh : connect to host localhost port 22:Connection refused
  4. Spring Cloud构建微服务架构:分布式服务跟踪(入门)
  5. 近期知识图谱顶会论文推荐,你都读过哪几篇?
  6. 互联网加大赛历届作品_匠心筑梦 ——家具漆服务系统2020年度涂装大赛成功举办...
  7. c++ new一个结构体_「C/C++」构造类型及应用:数组、结构体、共用体、枚举类型...
  8. ubuntu19 安装git_在Ubuntu 18.04上安装Git
  9. 【笔记】C++自学笔记系列02:类的组合与类的友元
  10. Flask模板参数传值的方法
  11. oracle逗号分隔函数
  12. Android中如何使按钮的背景变得透明
  13. Java面试高频题:Spring Boot+Sentinel+Nacos高并发已撸完
  14. dubbo/dubbox部署资料收集
  15. JavaWeb9大内置对象的作用与作用域
  16. 【不懂就问】ROST EA情感分析软件怎么操作
  17. QListView超简单的更新列表
  18. postgresql chm格式手册
  19. 为什么网线接法要分交叉连接和直连连接两种方式
  20. Netty Websocket多人多房间聊天室Demo

热门文章

  1. 一个字稳,云原生产品家族支撑冬奥会九大业务场景,打造云上奥运新体验
  2. OpenKruise v0.7.0 版本发布:新增周期任务分发控制器
  3. python format 槽中槽_printf中的槽和实参--对比python struct包
  4. windows下利用IIS搭建web和ftp服务以及防火墙配置
  5. 函授报计算机还是工商管理,函授本科行政管理工商管理经济管理分别都有哪......
  6. asmx 接受 ajax post,jQuery ajax调用web服务(asmx)触发认证弹出框
  7. 第四代计算机软件系统,第四代计算机是什么计算机
  8. 局域网屏幕共享_ShareMouse for Mac(鼠标键盘共享)
  9. datetimepicker 时间不更新_iOS 14.2 正式版推送:新增人体检测,这些功能值得更新...
  10. 【星球知识卡片】换脸算法和人脸驱动都有哪些核心技术,如何对其长期深入学习...