怎样编写简单的Web Custom Control
作者:Robert
联系方式:Robert_luoqing@hotmail.com
Web Custom Control的函数调用序列图(本人根据调用过程自制的序列图,有不对的地方望指正):
注意:
1、在序列图的区域(1)中的函数执行顺序是不能确定的。
2、区域(2)内的函数是当第一次用到内部的子控件时就会调用到该区域内的函数,用来保证在调用子控件之前已把子控件初始化了;另外,一定要注意到CreateChildrenControls函数,在调用过程中,如果不控制ChildrenControlsCreated变量,则系统会保证只执行一次(即调用多次EnsureChildControls函数,CreateChildrenControls也只会调一次,因为当调用一次后,ChildrenControlsCreated被设为true,以后执行EnsureChildControls就不会调用到CreateChildrenControls函数了)。
3、在SaveViewState函数的下面千万不要对ViewState赋值,因为系统在SaveViewState函数内把ViewState中的值写入(序列化)到Page中去了,如果在该函数之后更改了ViewState变量,则值是不会被序列化的。
4、INamingContainer指示接口的作用:上面没有标出,该接口可以使在CreateChildrenControls中创建的子控件具有保持PostBackData值的效果。比如:在CreateChildrenControls内有下面一段代码:
TextBox m_text=new TextBox();
m_text.Text="Test";
m_text.AutoPostBack=true;
this.Controls.Add(m_text);
base.CreateChildControls ();
第一次执行的时m_text内的值是“Test”,当用户在界面中把该文本框的值改为“Secord”以后,如果该自定义web控件实现了INamingContainer接口,Post back后值仍然是“Secord”,如果没有实现该接口,则值为“Test”(我只注意到这个差别,大家可以试一下,找出其他的差别);
5、在编写Web Custom Control时,除LoadViewState,SaveViewState这两个地方用使用ViewState进行序列化和反序列化外,其他的地方最好用类的非公有变量代替,这样有几点好处:1、代码清晰(避免了即有ViewState又有变量,有时会使人糊涂);2、避免了多次类型转换;3、清楚明了的知道有几个变量被列化(当然这在写用户Web控件的时然也一样)。
6、如果要在Web Custom Control内引发事件的话(非子控件引发的事件),一定要实现IPostBackEventHandler接口,下段代码可以说明怎样引发事件:
protected override void Render(HtmlTextWriter output)
{
output.Write("<a href=javascript:"+this.Page.GetPostBackEventReference(this,"first")+">postback</a>");
base.Render(output);
}
这段代码会输入“postback”的一个链接,当点击时就会把内容提交到服务器,这时就可以在RaisePostBackEvent(string eventArgument)函数内接收事件了,其中eventArgument是与Page.GetPostBackEventReference的第二个参数相对应。
由于时间有限,对于其他的接口或功能希望大家自己体会。
下面有一段数据导行栏的代码做为参考。
//****************************************************************
/*
* 要求属性:
* 1、记录总数
* 2、每页显示记录数
* 3、当前页数
*
*该数据导行样的功能:
*1、要求提供记录总数,记录数,当前页数的功能
*2、要求提供上一页,下一页,首页,尾页的导行按钮,并要给出导行事件
*3、要求根据当前页来显示导行按钮的显示状态。
*4、要求客户只输入属性一次,以后要求系统自动维护各属性的变化。
*5、要求控件对属性的序列化
* */
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel;
namespace CustomerTest
{
public delegate void DataBarClick(object e,int number);
/// <summary>
/// Summary description for DataBar.
/// </summary>
[DefaultProperty("TotalRecord"),
ToolboxData("<{0}:DataBar NumberPerPage=20 TotalRecord=0 CurrentPage=1 runat=server></{0}:DataBar>")]
public class DataBar : System.Web.UI.WebControls.WebControl,INamingContainer
{
protected System.Web.UI.WebControls.LinkButton LinkButtonPreview;
protected System.Web.UI.WebControls.LinkButton LinkButtonNext;
protected System.Web.UI.WebControls.LinkButton LinkButtonLast;
protected System.Web.UI.WebControls.LinkButton LinkButtonFirst;
private bool m_Design=true;
protected int m_TotalRecord;//总记录数
protected int m_CurrentPage=1;//当前显示的页号
protected int m_NumberPerPage;//每页显示记录数
//建立寻行数据页数的事件
public event DataBarClick databarClick;
/// <summary>
/// Get或Set数据的总记录数
/// </summary>
[Bindable(true),
Category("Data"),
DefaultValue(0)]
public int TotalRecord
{
get
{
return this.m_TotalRecord ;
}
set
{
if(value<0)
throw new Exception("给出的总记录数不能少于0");
this.m_TotalRecord=value;
}
}
/// <summary>
/// Get或Set当前显示的页号
/// </summary>
[Bindable(true),
Category("Data"),
DefaultValue(1)]
public int CurrentPage
{
get
{
return this.m_CurrentPage;
}
set
{
if(value<1)
throw new Exception("给出的当前页不能少于1");
this.m_CurrentPage = value;
}
}
/// <summary>
/// Get或Set每页显示记录数
/// </summary>
[Bindable(true),
Category("Data"),
DefaultValue(20)]
public int NumberPerPage
{
get
{
return this.m_NumberPerPage;
}
set
{
if(value<1)
throw new Exception("每页显示的记录数不能少于1");
this.m_NumberPerPage = value;
}
}
protected override void CreateChildControls()
{
Table l_table=new Table();//建立外层的Table
TableRow l_tbrow=new TableRow();//只有一行
l_table.Rows.Add(l_tbrow);
TableCell l_tbcell=new TableCell();//建立第一个列的内容
l_tbrow.Controls.Add(l_tbcell);
l_tbcell.Wrap=false;
LinkButtonFirst =new System.Web.UI.WebControls.LinkButton();
LinkButtonFirst.ID="LinkButtonFirst";
LinkButtonFirst.Text="首页";
LinkButtonFirst.Click+=new EventHandler(Guid_Click);
l_tbcell.Controls.Add(LinkButtonFirst);
l_tbcell=new TableCell();//建立"|"
l_tbrow.Controls.Add(l_tbcell);
l_tbcell.Text=" | ";
l_tbcell=new TableCell();//建立第二个列的内容
l_tbrow.Controls.Add(l_tbcell);
l_tbcell.Wrap=false;
LinkButtonPreview =new System.Web.UI.WebControls.LinkButton();
LinkButtonPreview.ID="LinkButtonPreview";
LinkButtonPreview.Text="上一页";
LinkButtonPreview.Click+=new EventHandler(Guid_Click);
l_tbcell.Controls.Add(LinkButtonPreview);
l_tbcell=new TableCell();//建立"|"
l_tbrow.Controls.Add(l_tbcell);
l_tbcell.Text=" | ";
l_tbcell=new TableCell();//建立第三个列的内容
l_tbrow.Controls.Add(l_tbcell);
l_tbcell.Wrap=false;
LinkButtonNext =new System.Web.UI.WebControls.LinkButton();
LinkButtonNext.ID="LinkButtonNext";
LinkButtonNext.Text="下一页";
LinkButtonNext.Click+=new EventHandler(Guid_Click);
l_tbcell.Controls.Add(LinkButtonNext);
l_tbcell=new TableCell();//建立"|"
l_tbrow.Controls.Add(l_tbcell);
l_tbcell.Text=" | ";
l_tbcell=new TableCell();//建立第四个列的内容
l_tbrow.Controls.Add(l_tbcell);
l_tbcell.Wrap=false;
LinkButtonLast =new System.Web.UI.WebControls.LinkButton();
LinkButtonLast.ID="LinkButtonLast";
LinkButtonLast.Text="尾页";
LinkButtonLast.Click+=new EventHandler(Guid_Click);
l_tbcell.Controls.Add(LinkButtonLast);
this.Controls.Add(l_table);
base.CreateChildControls ();
}
private void Guid_Click(object sender, System.EventArgs e)
{
setGuide(sender);
if(this.databarClick!=null)
{
this.databarClick(this,this.m_CurrentPage);
}
}
/// <summary>
/// 用于激发databarClick事件
/// </summary>
public void OndatabarClick()
{
if(this.databarClick!=null)
{
this.databarClick(this,this.m_CurrentPage);
}
}
/// <summary>
/// 这是用来显示导行栏中每个元素的状态
/// </summary>
/// <param name="sender">表示是按的那一个导行栏元素的按钮,如果不是由导行栏触发,则传null值</param>
private void setGuide(object sender)
{
int m_iTotalPage = Convert.ToInt32(System.Math.Ceiling(Convert.ToDouble(this.m_TotalRecord)/Convert.ToDouble(m_NumberPerPage)));
if(m_iTotalPage<this.m_CurrentPage)
this.m_CurrentPage=1;
if(sender!=null)
{
if(((Control)sender).ID=="LinkButtonFirst")
{
this.m_CurrentPage=1;
}
if(((Control)sender).ID=="LinkButtonNext")
{
this.m_CurrentPage=this.m_CurrentPage+1;
}
if(((Control)sender).ID=="LinkButtonPreview")
{
this.m_CurrentPage=this.m_CurrentPage-1;
}
if(((Control)sender).ID=="LinkButtonLast")
{
this.m_CurrentPage=m_iTotalPage;
}
}
this.LinkButtonFirst.Enabled=!(this.m_CurrentPage==1);
this.LinkButtonPreview.Enabled=!(this.m_CurrentPage==1);
this.LinkButtonLast.Enabled=!(this.m_CurrentPage==m_iTotalPage);
this.LinkButtonNext.Enabled=!(this.m_CurrentPage==m_iTotalPage);
}
protected override void OnLoad(EventArgs e)
{
this.m_Design=false;
base.OnLoad (e);
}
protected override void Render(HtmlTextWriter writer)
{
if(this.m_Design)
writer.Write("数据导行栏控件");
base.Render (writer);
}
protected override void LoadViewState(object savedState)
{
base.LoadViewState (savedState);
/*
* 在这里用于变量的反序列化,即从ViewState中取回
* 变量的值。
* */
this.m_CurrentPage=(int)this.ViewState["CurrentPage"];
this.m_NumberPerPage=(int)this.ViewState["NumberPerPage"];
this.m_TotalRecord=(int)this.ViewState["TotalRecord"];
}
protected override object SaveViewState()
{
/*
*这里用于把变量序列化,以使PostBack时进行恢复。
* */
this.ViewState["CurrentPage"]=this.m_CurrentPage;
this.ViewState["NumberPerPage"]=this.m_NumberPerPage;
this.ViewState["TotalRecord"]=this.m_TotalRecord;
return base.SaveViewState ();
}
protected override void OnPreRender(EventArgs e)
{
this.setGuide(null);
base.OnPreRender (e);
}
}
}
//**********************************************************************************
测试码:
Test.aspx
<%@ Register TagPrefix="test" NameSpace="CustomerTest" Assembly="TestDataBar" %>
<%@ Page language="c#" Codebehind="Test.aspx.cs" AutoEventWireup="false" Inherits="TestDataBar.Test" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<HTML>
<HEAD>
<title>Test</title>
<meta name="GENERATOR" Content="Microsoft Visual Studio .NET 7.1">
<meta name="CODE_LANGUAGE" Content="C#">
<meta name="vs_defaultClientScript" content="JavaScript">
<meta name="vs_targetSchema" content="http://schemas.microsoft.com/intellisense/ie5">
</HEAD>
<body MS_POSITIONING="GridLayout">
<form id="Form1" method="post" runat="server">
<test:DataBar runat="server" id="stDBar"></test:DataBar>
<FONT face="宋体"></FONT>
</form>
</body>
</HTML>
Test.aspx.cs
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
namespace TestDataBar
{
/// <summary>
/// Summary description for Test.
/// </summary>
public class Test : System.Web.UI.Page
{
protected CustomerTest.DataBar stDBar;
private void Page_Load(object sender, System.EventArgs e)
{
if(!this.IsPostBack)
{
this.stDBar.TotalRecord=100;
this.stDBar.NumberPerPage=15;
this.stDBar.CurrentPage=1;
}
}
#region Web Form Designer generated code
override protected void OnInit(EventArgs e)
{
//
// CODEGEN: This call is required by the ASP.NET Web Form Designer.
//
InitializeComponent();
base.OnInit(e);
}
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.stDBar.databarClick += new CustomerTest.DataBarClick(this.stDBar_databarClick);
this.Load += new System.EventHandler(this.Page_Load);
}
#endregion
private void stDBar_databarClick(object e, int number)
{
this.Response.Write("当前页是:第"+number.ToString()+"<br>");
this.Response.Write("总记录数是:"+this.stDBar.TotalRecord.ToString()+"<br>");
this.Response.Write("每页记录数是:"+this.stDBar.NumberPerPage.ToString()+"<br>");
}
}
}