Petshop的自定义控件

  

   说到自定义控件我们应该先了解清楚 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;

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()
  {
   

  }
  #region IPostBackDataHandler
成员

  public void RaisePostDataChangedEvent()
  {
   // 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;
  }

  #endregion

  #region IPostBackEventHandler 成员

  public void RaisePostBackEvent(string eventArgument)
  {
   // TODO: 
添加 Mycontrol.RaisePostBackEvent 实现
   
   Page.Response.Write("click!");

  }

  #endregion
 }
}
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">
   &nbsp;
   <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);

   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);
  }

 这里是创建子控件,两个翻页按钮,重载了基类的CreateControlHierarchy()方法;

另外一个地方

for (int i = 0, j = Controls.Count - 2; i < j; i++)
    Controls[i].RenderControl(writer);

这实际上与simplePager的base.Render(writer)效果一样.

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值