说到自定义控件我们应该先了解清楚
asp.net页面的生命周期
,简单的概括一下,当一个网页获取的请求:用户的提交或者超级链接发送到
WEB
服务器后都会引起页面从创建到处理完成的一系列事件。这些事情有,首先
初始化页面及这些页面控件对象,这些其实是由
OnInit
完成,所说的初始化就是为这些对象分配必须的内存罢了;导入
ViewState
数据,这是控件的一个重要的属性,包括了控件本身的各种信息;接下来的事件是比较重要的,凡是实现了
IPostBackDataHandler
接口的,都将被执行,这个接口实现的两个方法,
RaisePostDataChangedEvent()
和
LoadPostData(),
不过
RaisePostDataChangedEvent()
执行是在
LoadPostData()
返回
TRUE
后的,通知数据更新后
;
导入对象的
load
事件,在这个事件中,所有对象布置在
DOM
页面,对象可以很容易的从客户端获得诸如一些属性,我们可以
Onload
重载该事件;接下来处理客户端的
PostBack
事件,这里实现的接口
IPostBackEventHandler
,执行
RaisePostBackEvent()
方法。这些都是些主要的事件,接下来就是保存
ViewState ,Render
呈现
HTML
发送给浏览器显示了。
看一个简单的自定义控件的例子:
Mycontrol.cs
using System;
using System.Web.UI;
using System.Web.UI;
namespace contro
{
/// <summary>
/// Mycontrol 的摘要说明。
/// </summary>
public class Mycontrol:Control,IPostBackDataHandler,IPostBackEventHandler
{ private string msg;
public string Msg
{
get {return this.msg;}
set {this.msg=value;}
}
public event System.EventHandler Receive;
protected override void Render( System.Web.UI.HtmlTextWriter writer)
{
writer.Write("<input type=/"text/" name=/"{0}/" value=/"{1}/">",this.UniqueID,(string)ViewState["tt"]);
writer.Write("<input type=button οnclick=/"{0}/" value=/"click me/">",Page.GetPostBackEventReference(this));
}
public Mycontrol()
{
{
/// <summary>
/// Mycontrol 的摘要说明。
/// </summary>
public class Mycontrol:Control,IPostBackDataHandler,IPostBackEventHandler
{ private string msg;
public string Msg
{
get {return this.msg;}
set {this.msg=value;}
}
public event System.EventHandler Receive;
protected override void Render( System.Web.UI.HtmlTextWriter writer)
{
writer.Write("<input type=/"text/" name=/"{0}/" value=/"{1}/">",this.UniqueID,(string)ViewState["tt"]);
writer.Write("<input type=button οnclick=/"{0}/" value=/"click me/">",Page.GetPostBackEventReference(this));
}
public Mycontrol()
{
}
#region IPostBackDataHandler 成员
#region IPostBackDataHandler 成员
public void RaisePostDataChangedEvent()
{
// TODO: 添加 Mycontrol.RaisePostDataChangedEvent 实现
if(this.Receive!=null)
{ Receive(this,new EventArgs());}
{
// TODO: 添加 Mycontrol.RaisePostDataChangedEvent 实现
if(this.Receive!=null)
{ Receive(this,new EventArgs());}
}
public bool LoadPostData(string postDataKey, System.Collections.Specialized.NameValueCollection postCollection)
{
// TODO: 添加 Mycontrol.LoadPostData 实现
this.msg=postCollection[this.UniqueID];
ViewState["tt"]=this.msg.ToString();
return true;
}
{
// TODO: 添加 Mycontrol.LoadPostData 实现
this.msg=postCollection[this.UniqueID];
ViewState["tt"]=this.msg.ToString();
return true;
}
#endregion
#region IPostBackEventHandler
成员
public void RaisePostBackEvent(string eventArgument)
{
// TODO: 添加 Mycontrol.RaisePostBackEvent 实现
Page.Response.Write("click!");
{
// TODO: 添加 Mycontrol.RaisePostBackEvent 实现
Page.Response.Write("click!");
}
#endregion
}
}
WebForm1.aspx
}
}
WebForm1.aspx
<%@ Page language="c#" Codebehind="WebForm1.aspx.cs" AutoEventWireup="false" Inherits="contro.WebForm1" %>
<%@ Register TagPrefix="cc1" Namespace="contro" Assembly="contro"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<HTML>
<HEAD>
<title>WebForm1</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">
<cc1:Mycontrol runat="server" id="Mycontrol1"></cc1:Mycontrol>
<asp:Button id="Button1" style="Z-INDEX: 101; LEFT: 176px; POSITION: absolute; TOP: 64px" runat="server"
Text="Button"></asp:Button>
</form>
</body>
</HTML>
<%@ Register TagPrefix="cc1" Namespace="contro" Assembly="contro"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<HTML>
<HEAD>
<title>WebForm1</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">
<cc1:Mycontrol runat="server" id="Mycontrol1"></cc1:Mycontrol>
<asp:Button id="Button1" style="Z-INDEX: 101; LEFT: 176px; POSITION: absolute; TOP: 64px" runat="server"
Text="Button"></asp:Button>
</form>
</body>
</HTML>
这里实现了一个文本框和一个按钮的组合控件。
看完这里,相信我们看
Petshop
的自定义控件是一件很轻松的事情了,有两个重要的控件
SimplePager
ViewStatePager;
这两个控件的关系是
ViewStatePager
继承
SimplePager,
而
SimplePager
又继承
Repteater
控件
SimplePager
的代码:
using
System;
using
System.Collections;
using
System.Collections.Specialized;
using
System.Text;
using
System.Text.RegularExpressions;
using
System.Web.UI;
using
System.Web.UI.WebControls;
namespace
PetShop.Web.Controls {
public class SimplePager : Repeater {
//Static constants
protected const string HTML1 = "<table cellpadding=0 cellspacing=0><tr><td colspan=2>";
protected const string HTML2 = "</td></tr><tr class=gridNav><td>";
protected const string HTML3 = "</td><td align=right>";
protected const string HTML4 = "</td></tr></table>";
private static readonly Regex RX = new Regex(@"^&page=/d+", RegexOptions.Compiled);
private const string LINK_PREV = "<a href=?page={0}><img src=Images/buttonPrev.gif alt=Previous border=/"0/"></a>";
private const string LINK_MORE = "<a href=?page={0}><img src=Images/buttonMore.gif alt=More border=/"0/"></a>";
private const string KEY_PAGE = "page";
private const string COMMA = "?";
private const string AMP = "&";
protected string emptyText;
private IList dataSource;
private int pageSize = 10;
private int currentPageIndex;
private int itemCount;
override public object DataSource { // 重载数据源属性
set {
//This try catch block is to avoid issues with the VS.NET designer
//The designer will try and bind a datasource which does not derive from ILIST
try{
dataSource = (IList)value;
ItemCount = dataSource.Count;
}catch{
dataSource = null;
ItemCount = 0;
}
}
}
public int PageSize {
get { return pageSize; }
set { pageSize = value; }
}
protected int PageCount {
get { return (ItemCount - 1) / pageSize; }
}
virtual protected int ItemCount {
get { return itemCount; }
set { itemCount = value; }
}
virtual public int CurrentPageIndex {
get { return currentPageIndex; }
set { currentPageIndex = value; }
}
public string EmptyText {
set { emptyText = value; }
}
public void SetPage(int index) {
OnPageIndexChanged(new DataGridPageChangedEventArgs(null, index));
}
override protected void OnLoad(EventArgs e) {
if (Visible) {
string page = Context.Request[KEY_PAGE];
int index = (page != null) ? int.Parse(page) : 0;
SetPage(index);
}
}
///<summary>
/// Overriden method to control how the page is rendered
///</summary>
///<param name="writer"></param>
override protected void Render(HtmlTextWriter writer) {
//Check there is some data attached
if (ItemCount == 0) {
writer.Write(emptyText);
return;
}
//Mask the query
string query = Context.Request.Url.Query.Replace(COMMA, AMP);
query = RX.Replace(query, string.Empty);
// Write out the first part of the control, the table header
writer.Write(HTML1);
// Call the inherited method
base.Render(writer);
// Write out a table row closure
writer.Write(HTML2);
//Determin whether next and previous buttons are required
//Previous button?
if (currentPageIndex > 0)
writer.Write(string.Format(LINK_PREV, (currentPageIndex - 1) + query));
//Close the table data tag
writer.Write(HTML3);
//Next button?
if (currentPageIndex < PageCount)
writer.Write(string.Format(LINK_MORE, (currentPageIndex + 1) + query));
//Close the table
writer.Write(HTML4);
}
override protected void OnDataBinding(EventArgs e) {
//Work out which items we want to render to the page
int start = CurrentPageIndex * pageSize;
int size = Math.Min(pageSize, ItemCount - start);
IList page = new ArrayList();
//Add the relevant items from the datasource
for (int i = 0; i < size; i++)
page.Add(dataSource[start + i]);
//set the base objects datasource
base.DataSource = page;
base.OnDataBinding(e);
}
public event DataGridPageChangedEventHandler PageIndexChanged;
virtual protected void OnPageIndexChanged(DataGridPageChangedEventArgs e) {
if (PageIndexChanged != null)
PageIndexChanged(this, e);
}
}
}
PETSHOP的这个自定义SimplePager主要是实现了对实现了ILIST接口的数据源的绑定,同时实现了分页功能,如果我们不自定义控件的话,那只能用PagedDataSource类了,
{ PagedDataSource obj=new PagedDataSource();
obj.DataSource=ds;
obj.AllowPaging=true;
obj.PageSize=5;
obj.CurrentPageIndex=...}了,当然也可以用其他存取过程实现,SimplePager在MS追求性能的同时实际上也同时带来了一个弊端,就是数据都是一次性取出了再在SimplePager里面来刷选自己所要的数据,跟DataGrid自带的分页差不多,不过总的来说SimplePager再次取数据是从Cache里面取,总的性能还是不错的。以前对PageCount=(ItemCount-1)/PageSize到是没在意,今天看看才发现为什么要-1,呵呵,这里因为CurrentPageIndex我们设置从0开始的,也就是首页为0,所以我们的PageCount也应该从0开始,也就是PageCount=3时,实际上我们是分了4页,ItemCount-1实际上是为了假如刚好为最后一页是恰好PageSize这吗多的话,我们不就多分了一页,所以必须-1,当然如果我们的currentPageIndex也可以从1开始的话int start = CurrentPageIndex * pageSize; 变为int start = (CurrentPageIndex-1) * pageSize;应该ItemCount-1/PageSize +1;了
在这里我们可以看到前面说的那些控件实现的接口都在Repeater底层实现了,我们只需要重载虚拟函数就可以了,我们可以看到这里这个控件引用的翻页事件为:
public
event DataGridPageChangedEventHandler PageIndexChanged;为DataGrid的PageIndexChanged的事件处理方法,有点耐闷,但是我们知道,DataGridPageChangedEventArgs e DataGridPageChangedEventArgs类为
public sealed class DataGridPageChangedEventArgs : EventArgs
{
// Methods
public DataGridPageChangedEventArgs(object commandSource, int newPageIndex);
// Properties
public object CommandSource { get; }
public int NewPageIndex { get; }
// Fields
private object commandSource;
private int newPageIndex;
}
所以用这个事件也没什么奇怪的。
另外一个ViewStatePager实现的是ViewState保存页面状态的SimplePager,这个控件的翻页与上面的simplePager控件不同,利用了按钮的回发,这里主要是两个方法:
override protected void CreateControlHierarchy(bool useDataSource) {
base.CreateControlHierarchy(useDataSource);
base.CreateControlHierarchy(useDataSource);
btnPrev = new ImageButton();
btnPrev.ImageUrl = IMG_PREV;
btnPrev.AlternateText = ALT_PREV;
btnPrev.Click += new ImageClickEventHandler(PreviousClicked);
Controls.Add(btnPrev);
btnMore = new ImageButton();
btnMore.ImageUrl = IMG_MORE;
btnMore.AlternateText = ALT_MORE;
btnMore.Click += new ImageClickEventHandler(MoreClicked);
Controls.Add(btnMore);
}
btnPrev.ImageUrl = IMG_PREV;
btnPrev.AlternateText = ALT_PREV;
btnPrev.Click += new ImageClickEventHandler(PreviousClicked);
Controls.Add(btnPrev);
btnMore = new ImageButton();
btnMore.ImageUrl = IMG_MORE;
btnMore.AlternateText = ALT_MORE;
btnMore.Click += new ImageClickEventHandler(MoreClicked);
Controls.Add(btnMore);
}
这里是创建子控件,两个翻页按钮,重载了基类的
CreateControlHierarchy()方法;
另外一个地方
for (int i = 0, j = Controls.Count - 2; i < j; i++)
Controls[i].RenderControl(writer);
Controls[i].RenderControl(writer);
这实际上与simplePager的base.Render(writer)效果一样.