zoukankan      html  css  js  c++  java
  • 【转】Asp.net控件开发学习笔记整理篇

    最近一直在做MVC项目,对于WEBFORM 好像快忘记了。周末无聊,顺带看看他人的笔记。再次温习下。

    复习大纲:

    导航、页面生命周期及其它导论

    一、服务器控件生命周期

    二、控件开发基础

    三、Asp.net服务端状态管理

    四、Asp.net客户端状态管理

    五、数据回传

    六、WebControl基类

    七、服务器控件事件

    事件和委托之间的暧昧关系往往是大多Web Developer在学习.net中的一个里程碑,当明白事件和委托的关系后,.net水平往往就上了一个新的台阶。

    下面说到服务器控件的事件模型.

    在任何一个服务器编程开发框架中,事件都是解耦功能和具体实现的一剂良方,Asp.net当然也不例外。比如说吧,页面上的button的click事件表示它的功能,而具体的实现将会被分离交给Developer来进行具体实现。

    传统的编程模型和基于事件的编程模型可以用下图进行简约概括:

       
     

    我们可以看出事件极大的简化了编程工作,客户端程序只需要注册到事件并且和事件的签名保持一致(即参数个数和类型相同)即可。在事件发生后,客户端程序会被通知并执行相应实现(.net framework的事件正是观察者模式的最好例子:-)

    Asp.net通过ViewStateHttp Post协议巧妙的实现了让开发人员感觉貌似控件能像WinForm程序中那样记住自己的状态。这使Asp.net可以在不使用客户端javascript的情况下,而实现数据回传。

        

        上面的图例展示出TextBox通过暴露相应的事件来通知被注册的函数.还记得前面所说的IPostDataHandler接口嘛,大多数服务器控件的事件都是通过ViewState来将数据以Http Post协议传回服务器,服务器根据回传数据的不同来引发相应事件。因此我们可以看出Button控件生成的<input type=”submit” />是有往服务器提交的功能的,而其他控件比如DropDownList或者是Checkbox是没有像服务器提交的功能。因此引发服务器事件便无从谈起。Asp.net通过在客户端设置javascript事件来引发向服务器的Http Post回传。而这一切仅仅需要将AutoPostBack属性设置为true.

      .net FrameWork 事件模型

       .net framework提供了基于委托的使不同类之间进行异步交互的机制。下面先简单说一下事件的核心-------委托

    Delegate

    委托在一定程度上有点像接口,是在发布者和订阅者之间的一个协议。接口是制订类的数据成员以及成员函数的签名。而委托,是制订单个函数的签名.

    创建一个委托的实例是通过创建一个和委托相匹配的函数。

    MSDN里把委托比喻成类型安全的函数指针。但是委托不仅限于此,因为.net framework大大的扩展了这个“类型安全的指针”,在CLR via C#这本书里说委托的实质上是一个类。因此委托可以按照次序依次调用多个匹配的方法,无论是静态方法还是实例方法。

    委托有两个部分,委托的声明和委托实例的声明。

    委托的声明代码会像:

    public delegate void foo(string A);

    而委托实例的声明会像

    foo f=new foo(MethodName)

    下面通过一个简单的Demo说明一下:

    Demo 委托

        先写一个简单的类:  

    public class DelegateDemo
    
        {
    
            public delegate void DeleDemo(string a);
    
            public static void FunctionA(string a)
    
            {
    
                HttpContext.Current.Response.Write("静态方法,传入的参数是:" + a + "<br />");
    
            }
    
            public void FunctionB(string a)
    
            {
    
                HttpContext.Current.Response.Write("实例方法,传入的参数是:" + a + "<br />");
    
            }
    
    }
    

    下面是客户端代码:        

    DelegateDemo dd = new DelegateDemo();
    
    DelegateDemo.DeleDemo d = new DelegateDemo.DeleDemo(dd.FunctionB);
    
    d += DelegateDemo.FunctionA;
    
    d("参数");

    最后的输出结果为:

    实例方法,传入的参数是:参数
    静态方法,传入的参数是:参数

        通过上面小Demo可以发现委托不仅仅是“类型安全的指针”,并且委托是按照次序调用实例方法和静态方法.

    事件

       C#里有专门用于声明事件的event关键字,一个典型的事件生命会像:

    public event EventHandler click; 

    事件关键字后面是委托,再后面是事件名称,命名事件的名称最好是动词,表名某些事情发生了。比如click,Init,Load,TextChanged这样。由此可以看出事件其实就是特殊的委托。因为所有的事件都是继承于System.EventHandler.

    EventHandler 委托

    所有asp.net内置控件事件处理函数的签名都和EventHandler或者继承于它的子类保持一致。它的原型是:

    [SerializableAttribute]
    
    [ComVisibleAttribute(true)]
    
    public delegate void EventHandler(Object sender,EventArgs e)

    第一个参数表示引发事件的对象,第二个参数表示引发事件后所要传给处理程序的参数。

    在一般情况下,开发人员最好是按照这种签名格式来声明函数事件的委托.

    在控件内部声明事件后,你必须在需要的情况下引发事件,直接引发事件是非常不好的做法。而在asp.net预定义的控件中都使用了如下方法:

     声明一个virtual protected void的方法,命名方式为On+事件名称.下面是一个例子:

          

    protected virtual void OnClick(EventArgs e)
    
            {
    
                if (Click != null)
    
                    Click(this, e);
    
           }
    

    这个方法首先做的是先检查客户端方法是否注册,如果有客户端方法进行了注册,则引发事件。

    EventCollection

    如果在单个控件中有多个事件,那么使用System.ComponentModel.EventHandlerList对事件进行保存将会在内存占用上有不错的提高。EventHandlerList对一个类内发布多个事件提供了一个列表容器。下面是多个事件和使用EventHandlerList的对比示意:

      

    第一步是实例化一个EventHandlerList的实例:

    protected EventHandlerList eventList = new EventHandlerList();

    第二步是声明一个容器用于保存事件的key

    private static readonly object ClickEvent = new object();

    最后一步是像往常一样声明一个事件,但有所不同的是就像属性的get和set程序块一样,对于事件C#提供了add和remove关键字:

    public event EventHandler Click
    
            {
    
                add
    
                {
    
                    Events.AddHandler(ClickEvent, value);
    
                }
    
                remove
    
                {
    
                    Events.RemoveHandler(ClickEvent, value);
    
                }
    
          }
    

    而在这时的事件调用方法就会像下面代码:

    protected virtual void OnClick(EventArgs e)
    
            {
    
                EventHandler clickEventDelegate = (EventHandler)Events[ClickEvent];
    
                if (clickEventDelegate != null)
    
                 {
    
                    clickEventDelegate(this, e);
    
                }
    
         }
    

    上面代码首先从事件列表中通过索引器以第一步中保存事件的key为参数提取出事件并检查客户端是否注册到此事件,如果是,则激发事件。

     Command事件和事件冒泡

        Command事件是System.Web.UI.WebControls命名空间里的强大模式。这个最好的例子是GridView


        

     在GridView的Row里嵌套的button点击会触发Command事件,后台可以根据CommandArgument的不同来决定是执行edit操作还是delete操作等。而事件冒泡有些像javascript里的事件冒泡,但有所不同的是这里的事件冒泡到能够处理这个事件的地方停止,比如上图中command事件会冒泡到DataGrid里的ItemCommand里停止,因为ItemCommand事件可以对command事件进行处理.

     在定义Command事件时会和前面大同小异,不同之处在于首先需要一个继承与System.EventArgs的CommandEventArgs类来进行参数传递,代码如下

    public class CommandEventArgs : EventArgs
            {
                public CommandEventArgs(string _commandName, string _commandArgument)
                {
                    CommandName = _commandName;
                    CommandArgument = _commandArgument;
                }
    
                private string commandname;
                private string commandArgument;
                public virtual string CommandName
                {
                    get
                    {
                        return commandname;
                    }
                    set
                    {
                        commandname = value;
                    }
                }
                public virtual string CommandArgument
                {
                    get
                    {
                        return commandArgument;
                    }
                    set
                    {
                        commandArgument = value;
                    }
                }
            }
    

      然后在需要定义的控件里定义这两个属性,代码如下:

             

    public virtual string CommandName
            {
                get
                {
                    object name = ViewState["CommandName"];
                    if (name == null)
                        return string.Empty;
                    else
                        return (string)name;
                }
                set
                {
                    ViewState["CommandName"] = value;
                }
            }
            public virtual string CommandArgument
            {
                get
                {
                    object arg = ViewState["CommandArgument"];
                    if (arg == null)
                        return string.Empty;
                    else
                        return (string)arg;
                }
                set
                {
                    ViewState["CommandArgument"] = value;
                }
            }
    

    然后重复前面的步骤,在控件内部定义命令事件:

    private static readonly object CommandKey = new object();
    
            public event CommandEventHandler Command
            {
                add
                {
                    Events.AddHandler(CommandKey, value);
                }
                remove
                {
                    Events.RemoveHandler(CommandKey, value);
                }
            }
    

    最后一步和前面说的引发事件的OnXXX的实现都略有不同,这里在控件内部实现的代码如下:       

    protected virtual void OnCommand(CommandEventArgs ce)
            {
                CommandEventHandler commandEventDelegate = (CommandEventHandler)Events[CommandKey];
                if (commandEventDelegate != null)
                {
                    commandEventDelegate(this, ce);
                }
                RaiseBubbleEvent(this, ce);
            }
    

    注意最后一句,RaiseBubbleEvent方法.这个方法可以将控件的事件传递到它的父容器上。

     到这里很多人都会好奇,那CommandName和CommandArgument两个参数是如何传入到CommandEventArgs里去的呢?

     其实是在引发事件时传入的,代码如下:

      OnCommand(new CommandEventArgs(CommandName, CommandArgument));

    DEMO 带Command事件的Button

        其实这个Demo就是把上面的代码全部拼装起来,代码可能会有点长,代码如下:    

    namespace DemoButton
    {
        using System;
    
        [ToolboxData("<{0}:superbutton runat=server></{0}:superbutton>")]
        public class ButtonDemo : Control, IPostBackEventHandler
        {
            public delegate void CommandEventHandler(object sender, CommandEventArgs e);
            public virtual string Text
            {
                get
                {
                    object text = ViewState["Text"];
                    if (text == null)
                        return string.Empty;
                    else
                        return (string)text;
                }
                set
                {
                    ViewState["Text"] = value;
                }
            }
            private static readonly object ClickKey = new object();
            public event EventHandler Click
            {
                add
                {
                    Events.AddHandler(ClickKey, value);
                }
                remove
                {
                    Events.RemoveHandler(ClickKey, value);
                }
            }
            protected virtual void OnClick(EventArgs e)
            {
                EventHandler clickEventDelegate = (EventHandler)Events[ClickKey];
                if (clickEventDelegate != null)
                {
                    clickEventDelegate(this, e);
                }
            }
            private static readonly object CommandKey = new object();
            public event CommandEventHandler Command
            {
                add
                {
                    Events.AddHandler(CommandKey, value);
                }
                remove
                {
                    Events.RemoveHandler(CommandKey, value);
                }
            }
            public virtual string CommandName
            {
                get
                {
                    object name = ViewState["CommandName"];
                    if (name == null)
                        return string.Empty;
                    else
                        return (string)name;
                }
                set
                {
                    ViewState["CommandName"] = value;
                }
            }
            public virtual string CommandArgument
            {
                get
                {
                    object arg = ViewState["CommandArgument"];
                    if (arg == null)
                        return string.Empty;
                    else
                        return (string)arg;
                }
                set
                {
                    ViewState["CommandArgument"] = value;
                }
            }
            protected virtual void OnCommand(CommandEventArgs ce)
            {
                CommandEventHandler commandEventDelegate = (CommandEventHandler)Events[CommandKey];
                if (commandEventDelegate != null)
                {
                    commandEventDelegate(this, ce);
                }
                RaiseBubbleEvent(this, ce);
            }
    
            public void RaisePostBackEvent(string argument)
            {
                OnCommand(new CommandEventArgs(CommandName, CommandArgument));
                //OnClick(EventArgs.Empty);
            }
    
            protected override void Render(HtmlTextWriter writer)
            {
                base.Render(writer);
                Page.VerifyRenderingInServerForm(this);
                writer.Write("<INPUT type=""submit""");
                writer.Write(" name=""" + this.UniqueID + """");
                writer.Write(" id=""" + this.UniqueID + """");
                writer.Write(" value=""" + Text + """");
                writer.Write(" />");
            }
        }
    
        public class CommandEventArgs : EventArgs
        {
            public CommandEventArgs(string _commandName, string _commandArgument)
            {
                CommandName = _commandName;
                CommandArgument = _commandArgument;
            }
            private string commandname;
            private string commandArgument;
            public virtual string CommandName
            {
                get
                {
                    return commandname;
                }
                set
                {
                    commandname = value;
                }
            }
            public virtual string CommandArgument
            {
                get
                {
                    return commandArgument;
                }
                set
                {
                    commandArgument = value;
                }
            }
        }
    }
    

      

    前台代码:

    首先注册页面控件:

     <%@ Register Namespace="DemoButton" TagPrefix="cc" %>

    前台代码:

       <cc:ButtonDemo runat="server" Text="第一个按钮" ID="bt1" CommandName="bt1"

            CommandArgument="第一个button的参数" oncommand="bt1_Command" ></cc:ButtonDemo>

          

     <cc:ButtonDemo runat="server" Text="第二个按钮" ID="bt2" CommandName="bt1"

            CommandArgument="第二个button的参数" oncommand="bt1_Command" ></cc:ButtonDemo>

    事件处理程序:

        protected void bt1_Command(object sender, DemoButton.CommandEventArgs e)

        {

            if (e.CommandName == "bt1")

            {

                Response.Write("第一个button被点击了,参数是"+e.CommandArgument);

            }

            else if (e.CommandArgument == "bt12")

            {

                Response.Write("第二个button被点击了,参数是" + e.CommandArgument);

            }

    }

    Demo的结果很简单,就不演示了:-)

  • 相关阅读:
    什么是ORM
    ORM优缺点
    Azure 中快速搭建 FTPS 服务
    连接到 Azure 上的 SQL Server 虚拟机(经典部署)
    在 Azure 虚拟机中配置 Always On 可用性组(经典)
    SQL Server 2014 虚拟机的自动备份 (Resource Manager)
    Azure 虚拟机上的 SQL Server 常见问题
    排查在 Azure 中新建 Windows 虚拟机时遇到的经典部署问题
    上传通用化 VHD 并使用它在 Azure 中创建新 VM
    排查在 Azure 中新建 Windows VM 时遇到的部署问题
  • 原文地址:https://www.cnblogs.com/taoqianbao/p/3486267.html
Copyright © 2011-2022 走看看