3.1 System.Web.UI.Control类介绍
3.1.1 Render方法
基类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属性的值为true。当ChildControlsCreated属性为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事件的触发函数。