惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V
V2EX
J
Java Code Geeks
MongoDB | Blog
MongoDB | Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Blog — PlanetScale
Blog — PlanetScale
A
About on SuperTechFans
Engineering at Meta
Engineering at Meta
美团技术团队
N
Netflix TechBlog - Medium
月光博客
月光博客
博客园 - 三生石上(FineUI控件)
F
Fortinet All Blogs
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
MyScale Blog
MyScale Blog
aimingoo的专栏
aimingoo的专栏
Last Week in AI
Last Week in AI
The Cloudflare Blog
量子位
Apple Machine Learning Research
Apple Machine Learning Research
小众软件
小众软件
腾讯CDC
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
D
DataBreaches.Net
H
Hackread – Cybersecurity News, Data Breaches, AI and More

博客园 - xiaolei1982

asp.net的ajax的实现 spring:bean生命周期(转贴) 在myeclipse下整合spring和hibernate(转贴) Struts+Spring+Hibernate练习(转贴) spring:bean注入(转贴) - xiaolei1982 - 博客园 解析eclipse下生成Hibernate DAO中的几个方法(转贴) ASP.NET ViewState 实现分析(转贴) 自定义控件源码(转贴) ASP.NET服务器控件的开发(4)(转贴) ASP.NET服务器控件的开发(3)(转贴) ASP.NET服务器控件的开发(2) (转贴) Web 页本是无状态而断续的(转贴) asp.net控件开发基础(3)(转贴) asp.net控件开发基础(6)(转贴) asp.net控件开发基础(5)(转贴) asp.net控件开发基础(4)(转贴) asp.net控件开发基础(2)(转贴) asp.net控件开发基础(1)(转贴) C#中使用反射显示程序集的所有类型和属性(转贴)
ASP.NET服务器控件的开发(1)(转贴)
xiaolei1982 · 2008-04-19 · via 博客园 - xiaolei1982

基类System.Web.UI.Control最重要的方法是Render,代码如下:

          protected virtual void Render(HtmlTextWriter writer)
          {

                    this.RenderChildren(writer);

          }

它允许为一个HtmlTextWriter对象提供服务器控件的内容,并把内容输出到客户设备上。开发服务器控件时,我们可以重写该方法生成ASP.NET页面的内容

     在它的执行体中只有一行代码,调用了RenderChildren方法,该方法的作用是呈现该控件下的所有子控件。RenderChildren中的参数是HtmlTextWriter类。

     这里我们来简单介绍下System.Web.UI.HtmlTextWriter类,HtmlTextWriter类允许编写HTML内容和文本,我们来看一下HtmlTextWriter类的重要方法(表3-1)

表3-1

AddAttribute

允许在HtmlTextWrite输出流中添加HTML特性和值

AddStyleAttribute

允许在HtmlTextWrite输出流中添加HTML样式特性

WriteAttribute

允许在HtmlTextWrite输出流中写一个HTML特性

RenderBeginTag

允许写一个HTML元素开始标记到输出流中

RenderEndTag

允许写一个结束标记到输出流中

WriteBeginTag

与RenderBeginTag方法类似,不同的是不能为元素写结束字符”>”

WriteEndTag

允许写一个已用RenderBeginTag方法写好的HTML元素的结束标记

Write

允许将值直接写入输出流

下面我们来看一个重写Render方法创建服务器控件的例子(例3-1),代码如下:

例3-1

using System;

using System.Web.UI;

using System.Web.UI.WebControls;

using System.ComponentModel;

namespace WebControlLibrary3

{

     [DefaultProperty("Text"),

         ToolboxData("<{0}:WebCustomControl1 runat=server></{0}:WebCustomControl1>")]

     [ParseChildren(false)] //将子XML元素视为子控件

     public class WebCustomControl1 : System.Web.UI.Control

     {

          private string text;

         [Bindable(true),

              Category("Appearance"),

              DefaultValue("")]

         public string Text

         {

              get

              {

                   return text;

              }

              set

              {

                   text = value;

              }

         }

         protected override void Render(HtmlTextWriter writer)

         {

              writer.AddAttribute(System.Web.UI.HtmlTextWriterAttribute.Type,"text");

              writer.AddAttribute(System.Web.UI.HtmlTextWriterAttribute.Class,"class1");

              writer.AddAttribute(System.Web.UI.HtmlTextWriterAttribute.Name,"text1");

              writer.AddAttribute(System.Web.UI.HtmlTextWriterAttribute.Value,this.Text);

              writer.AddStyleAttribute(System.Web.UI.HtmlTextWriterStyle.Height,"30");

              writer.AddStyleAttribute(System.Web.UI.HtmlTextWriterStyle.Width,"160");

              writer.AddStyleAttribute(System.Web.UI.HtmlTextWriterStyle.FontSize,"24");

              writer.RenderBeginTag(System.Web.UI.HtmlTextWriterTag.Input);

              writer.RenderEndTag();     

         }

     }

}

控件的调用代码:

<%@ Register TagPrefix="cc1" Namespace="WebControlLibrary3" Assembly="WebControlLibrary3" %>

……

……

……

<cc1:WebCustomControl1 id="WebCustomControl11"  Text=”test”  runat="server" >

<asp:Button id="Button1" runat="server" Text="Button" Width="104px" Height="32px"></asp:Button>

</cc1:WebCustomControl1>

显示结果如图(图3-1)

图3-1

 [ParseChildren(false)]这个属性是设置将子XML元素视为控件的属性还是视为子控件

     在这个例子中我们没有调用RenderChildren方法,所以该控件下的所有子控件都不会被呈现,所以在ASPX页的控件中添加<asp:Button id="Button1" runat="server" Text="Button" Width="104px" Height="32px"></asp:Button>是没用的,当我们在Render执行体中中调用RenderChildren方法

         protected override void Render(HtmlTextWriter writer)

         {

              ……

              writer.RenderEndTag();     

              RenderChildren(writer);

         }

那么显示结果就不同了,如图(图3-2)

图3-2

     看了这个例子你应该会感到编写服务器控件其实也很简单吧!

3.1.2     CreateChildControls方法

    基类System.Web.UI.Control另一个重要的方法是CreateChildControls方法,代码如下:

          protected virtual void CreateChildControls()

          {

          }

该方法没有任何的执行体,我们可以重写该方法为控件添加子控件,下面我们先来看一个例子(例3-2)

例3-2

using System;

using System.Web.UI;

using System.Web.UI.WebControls;

using System.ComponentModel;

namespace WebControlLibrary4

     [DefaultProperty("Text"),

         ToolboxData("<{0}:WebCustomControl1 runat=server></{0}:WebCustomControl1>")]

     public class WebCustomControl1 : System.Web.UI. Control

     {

         private string text;

         [Bindable(true),

              Category("Appearance"),

              DefaultValue("")]

         public string Text

         {

              get

              {

                   return text;

              }

              set

              {

                   text = value;

              }

         }

         protected override void CreateChildControls()

         {

              TextBox textBox1=new TextBox();

              textBox1.Text=this.text;

              this.Controls.Add(textBox1); //为控件添加子控件

         }

     }

}

控件的调用代码:

<%@ Register TagPrefix="cc1" Namespace="WebControlLibrary4" Assembly="WebControlLibrary4" %>

<cc1:WebCustomControl1 id="WebCustomControl11"  runat="server"  Text="例3-2">

</cc1:WebCustomControl1>

显示结果如图(图3-3)

图3-3

    在这个例子中我们通过重写CreateChildControls方法为控件添加了TextBox控件,

this.Controls.Add(textBox1)该语句是把TextBox控件添加到我们创建的服务器控件的子控件列属性中,这样我们才可以在页面显示该子控件的内容。

3.1.3     Control类其他方法介绍

     RaiseBubbleEvent方法可被派生类调用,把事件提升到控件层次结构上,发送给它的命名容器,该方法在基本控件类Control中的代码如下:

protected void RaiseBubbleEvent(object source, EventArgs args)

{

      for (Control control1 = this._parent; control1 != null; control1 = control1.Parent)

      {

            if (control1.OnBubbleEvent(source, args)) //触发父控件的OnBubbleEvent方法

            {

                  return;

            }

      }

}

调用该方法我们可以触发父控件的OnBubbleEvent方法,该方法我们将在下面介绍

我们先来看下Button控件OnCommand方法的代码:

protected virtual void OnCommand(CommandEventArgs e)

{

      CommandEventHandler handler1 = (CommandEventHandler) base.Events[Button.EventCommand];

      if (handler1 != null)

      {

            handler1(this, e); //触发Command事件

      }

      base.RaiseBubbleEvent(this, e); //将事件提升到控件层次结构上

}

Button控件的OnCommand方法中调用了控件基本类的RaiseBubbleEvent方法,当Button控件做为其他可以接收事件提升的控件的子控件时, RaiseBubbleEvent方法调用了父控件的OnBubbleEvent方法,传递的参数是在RaiseBubbleEvent方法中指明的

     OnBubbleEvent方法是用来捕获从子程序提升的事件,只被派生类调用,该方法在基本控件类Control中的代码如下:

protected virtual bool OnBubbleEvent(object source, EventArgs args)

{

      return false;

}

该方法是在子控件的RaiseBubbleEvent方法中被调用的,传递的参数也就是子控件RaiseBubbleEvent方法中的2个参数。

我们可以重写该方法接收提示事件的通告,通过检查事件参数来获取它们的类型和事件类型,并求解参数的内容,决定采取什么动作。

     OnBubbleEvent 方法有个Boolean返回值,这个返回值指示提升事件是否已被处理。因此控件能够处理某个被提升的事件,并允许其他事件继续提升到控件层次结构上,当返 回值为true时表明提升的事件已被处理,事件将不会被继续提升到控件的父控件中,当返回值为false时,事件将继续被提升。

     EnsureChildControls方法可被派生类调用来保证子控件被创建,同时设定
ChildControlsCreated属性的值为trueChildControlsCreated属性为true时,CreateChildControls
方法将不会被再次自动调用。

     RenderChildren方法在第一节我们已经有提到,该方法一般在重写Render方法时被调用,保证控件的子控件内容被输出到提供的System.Web.UI.HtmlTextWriter对象,从而在页面呈现子控件。

        LoadViewState方法在控件被初始化后调用。该方法的作用是使用__VIEWSTATE隐藏表单域提交的名-值数据对生成ViewState属性包,该属性我们会在下面介绍。

        SaveViewState方法在生成__VIEWSTATE隐藏表单域之前被调用可以修改服务器控件的ViewState属性。该方法与LoadViewState方法结合使用,我们重写这2个方法可以自定义在服务器控件中创建,管理和保存状态的方法。

     DataBind方法OnDataBinding方法,这两个方法与数据绑定有关,这里我们先不做分析,在下面的数据绑定一节我们会对这两个方法进行讲解

3.1.4     Control类的主要属性

     Controls属性,该属性是只读的,可以被继承重写。在例3-2中我们已经用到了该属性,通过该属性值的Add方法我们可以为控件添加子控件。添加代码如Controls.Add(textBox1);我们将一个名字为textBox1的TextBox控件添加进了当前控件的子控件列中。

     Parent属性,该属性是只读的,可以被继承重写。通过该属性我们可以得到当前控件的父控件。也许有人会问既然这个属性是只读的,那该属性是怎么被设置的呢?其实不用我们人为设置,Control类有一个名为_parent的私有成员,当我们调用Controls.Add(textBox1);将textBox1子控件添加时,自动会把当前控件设置为textBox1控件的父控件的,所以我们只要把textBox1添加为当前控件的子控件,我们就可以直接用Parent属性得到textBox1控件的父控件。

NamingContainer属性该属性是只读的,可以被继承重写。通过该属性我们可以得当当前控件的命名容器,也就是一个继承了INamingContainer接口的父控件。

     Visible属性,该属性的默认值为true,即控件内容会呈现在页上,当我们设置该属性为
false时,控件将不会在页面上显示。
     ChildControlsCreated属性,通过该属性我们可以设置或获取自控件是否已被创建,例如
我们在方法中设置了ChildControlsCreated属性为true那么CreateChildControls方法将不会被再次
调用,我们再做可数据绑定的控件时该属性将非常有用。
     ViewState属性,该属性我们将再下面分节讲(3.1.5节)
3.1.5     ViewState
     ViewState是控件类的一个属性,属于保护成员,可以被继承访问,类型为StateBag类型,存储
名-值对,他们一般用在回送中存储控件属性的值。
     看起来该属性好象是一个新技术,其实只是以旧创新,只是在控件执行周期的最后将ViewState中
的信息以一个HTML隐藏表单控件__VIEWSTATE写到客户机上。
     所以在ASPX页面呈现代码里我们可以看到隐藏表单语句如下:
<input type="hidden" name="__VIEWSTATE" value="dDwtODExODIwMDE1Ozs+m9YKwTpuJ4WsxzTv5/6V2+2RvFE=" />
     在回送时,这个信息从__VIEWSTATE隐藏表单控件读出,继续以ViewState名-值对的形式保存。
下面我们用2个例子来做下对比说明(例3-3和例3-4)
例3-3:

using System;

using System.Web.UI;

using System.Web.UI.WebControls;

using System.ComponentModel;

namespace WebControlLibrary4

{

     [DefaultProperty("Text"),

         ToolboxData("<{0}:WebCustomControl1 runat=server></{0}:WebCustomControl1>")]

     public class WebCustomControl1 : System.Web.UI.Control

     {

         [Bindable(true),

              Category("Appearance"),

              DefaultValue("")]

         public string Text

         {

              get

              {

                   if(this.ViewState["text"]==null)

                   {

                       this.ViewState["text"]="初始值";

                   }

                   return ViewState["text"].ToString();

              }

              set

              {

                   this.ViewState["text"]=value;

              }

         }

         protected override void Render(HtmlTextWriter writer)

         {

              writer.AddAttribute(HtmlTextWriterAttribute.Type,"text");

              writer.AddAttribute(HtmlTextWriterAttribute.Name,this.UniqueID);

              writer.AddAttribute(HtmlTextWriterAttribute.Value,this.Text);

              writer.RenderBeginTag(HtmlTextWriterTag.Input);

              writer.RenderEndTag();

         }

     }

}
控件的调用代码:
<%@ Register TagPrefix="cc1" Namespace="WebControlLibrary4" Assembly="WebControlLibrary4" %>
……
<cc1:WebCustomControl1 id="WebCustomControl11" runat="server"></cc1:WebCustomControl1>
<asp:Button id="Button1" style="Z-INDEX: 101; LEFT: 48px; POSITION: absolute; TOP: 64px" runat="server" Text="Button" Width="72px">
</asp:Button>
<asp:Button id="Button2" style="Z-INDEX: 102; LEFT: 64px; POSITION: absolute; TOP: 128px" runat="server" Text="Button2" Width="80px">
</asp:Button>
后置代码如下:

省略……

namespace WebApplication11

{

     public class WebForm1 : System.Web.UI.Page

     {

         protected WebControlLibrary4.WebCustomControl1 WebCustomControl11;

         protected System.Web.UI.WebControls.Button Button2;

         protected System.Web.UI.WebControls.Button Button1;

省略……

         private void InitializeComponent()

         {   

              this.Button1.Click += new System.EventHandler(this.Button1_Click);

         }

         private void Button1_Click(object sender, System.EventArgs e)

         {

              WebCustomControl11.Text="test";

         }

     }

}
看下运行结果,如图(图3-4)
图3-4
 
当我们点击Button显示如图(图3-5)
图3-5
 
当我们再点击Button2显示结果不变,仍为图3-5所示
     再来让我们看看例3-4,仔细观察下他们代码的区别。
例3-4:

//以上代码如例子1-3不变

     public class WebCustomControl1 : System.Web.UI.Control

     {

         private string text="初始值";

         [Bindable(true),

              Category("Appearance"),

              DefaultValue("")]

         public string Text

         {

              get

              {

                       return this.text;

              }

              set

              {

                   this.text=value;

              }

         }

//以下代码如例子3-3不变

我们对例3-3做了下小小的修改,在这里我们没有使用ViewState["text"]保存信息,那么显示结果会怎样呢?看看吧(图3-6)
图3-6
 
没区别啊!!!
点击下Button如图(图3-7)
图3-7
 
还是,没区别啊!!!
我们最后再点击下Button2如图(图3-8)
图3-8
 
呀,怎又变成图3-6的样子了啊?
     其实例3-3与例3-4的区别就在这里,因为例3-3我们使用了ViewState属性以名-值对的方式来存储Text属性的值,所以当我们
点击Button2按扭的时候代码
writer.AddAttribute(HtmlTextWriterAttribute.Value,this.Text);

读取的Text属性其实是ViewState["text"]值,该值是由隐藏表单控件__VIEWSTATE回送回来的,而例3-4没有使用ViewState属性保存信息,所以当点击Button2按扭时读取的Text属性其实时私有字符串变量text的初始值。

    当我们做可以供用户提交的控件时该属性时非常有用的,可以保存用户输入的信息,具体什么时候用,怎么用还是要仔细琢磨的。

3.1.6          Control类的主要事件

     Control类中定义了6个事件Init,Load,Unload,DataBinding,PreRender和Disposed。
看名字应该能猜到他们有什么用,
在什么时候触发的吧。这6个事件的触发函数分别为OnInit,OnLoad,OnUnload,OnDataBinding,
OnPreRender和Disposed。这6个方法都可以被继承重写,一般我们在编写控件时会重写OnInit方法
来实例化我们的子控件,要记住我们最好不要重写OnLoad方法来实例化我们的子控件,因为OnLoad
方法在控件被加载时都会调用一次,这样每次都会对子控件进行new,这样子控件的信息讲无法被
保存,而OnInit方法就不一样了,大家可以自己试下。

    DataBinding事件载数据绑定时会被触发,页就是在调用DataBind方法时被触发,在数据绑定节我们会用到DataBinding事件的触发函数。