关闭

Petshop的自定义控件

标签: stringhtmldatagridwebformquerybutton
961人阅读 评论(0) 收藏 举报
分类:
   说到自定义控件我们应该先了解清楚 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 onclick=/"{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)效果一样.
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:630930次
    • 积分:7147
    • 等级:
    • 排名:第3124名
    • 原创:32篇
    • 转载:265篇
    • 译文:0篇
    • 评论:90条
    最新评论