一、控件也是类
【效果】
【操作步骤】
1、 新建网站Web
2、 添加类CustomDataList.cs(系统会提示你把类建在App_Code文件夹中),代码如下:
using System; using System.Collections; using System.Text.RegularExpressions; using System.Web.UI; using System.Web.UI.WebControls; namespace WestGarden.Web { public class CustomDataList : DataList { } } |
3、在Default.aspx中注册并添加类CustomDataList,代码如下:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %> <%@ Register Namespace="WestGarden.Web" TagPrefix="cc" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title>具有分页功能的自定义DataList控件</title> </head> <body> <form id="form1" runat="server"> <div> <cc:CustomDataList ID="CustomDataList1" runat="server" RepeatColumns="2"> <ItemTemplate> <table border="1"> <tr> <td><%# Eval("Number")%></td> </tr> </table> </ItemTemplate> </cc:CustomDataList> </div> </form> </body> </html> |
4、在Default.aspx.cs中创建符合IList接口的表格示例数据,并做为数据源与CustomDataList1绑定,代码如下:
using System; using System.Data; using System.Collections; using System.Web.UI.WebControls; public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { CustomDataList1.DataSource = CreateDataSource(); CustomDataList1.DataBind(); } //创建符合IList接口的表格示例数据 IList CreateDataSource() { DataTable dt = new DataTable(); DataRow dr; dt.Columns.Add(new DataColumn("Number", typeof(Int32))); for (int i = 0; i <10; i++) { dr = dt.NewRow(); dr["Number"] = i; dt.Rows.Add(dr); } DataView dv = new DataView(dt); return dv; } } |
5、在浏览器中查看运行结果如效果图示。
【说明】
1、很多时候,怎么说都说不清楚的事情,做出来,什么都不说,大家也就都明白了。在这里,大家可以清清楚楚地看到,所谓的控件,完完全全地是个类。所谓的类,其实就是具有一定功能,可以进行某类操作的程序块,这个程序块可以有变量、属性、可以有函数、代码,当然也可以有窗体、界面。
2、为了演示方便,我们没有从数据库中读取数据,而是做了一个函数,动态创建一个具有Ilist接口的简单的表格数据。
3、DataList的模板和Repeater差不多,还是手工做,方便一些。和Repeater相比,主要多了一个RepeatColumns属性,在电子商务系统中,用来展示商品列表比较方便,但默认没有分页功能,可以通过本例,自定义实现。
二、呈现
【效果】
【操作步骤】
1、CustomDataList.cs中改写基类的Render()函数来实现,完整代码如下:
using System; using System.Collections; using System.Text.RegularExpressions; using System.Web.UI; using System.Web.UI.WebControls; namespace WestGarden.Web { public class CustomDataList : DataList { protected const string HTML1 = "<table border=1><tr><td colspan=2>"; protected const string HTML2 = "</td></tr><tr><td class=paging align=left>"; protected const string HTML3 = "</td><td align=right class=paging>"; 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}>< 上一页</a>"; private const string LINK_MORE = "<a href=?page={0}>下一页 ></a>"; private const string KEY_PAGE = "page"; private const string COMMA = "?"; private const string AMP = "&"; private int currentPageIndex = 0; override protected void Render(HtmlTextWriter writer) { string query = ""; if (!DesignMode) { query = Context.Request.Url.Query.Replace(COMMA, AMP); query = RX.Replace(query, string.Empty); } writer.Write(HTML1); base.Render(writer); writer.Write(HTML2); writer.Write(string.Format(LINK_PREV, (currentPageIndex - 1) + query)); writer.Write(HTML3); writer.Write(string.Format(LINK_MORE, (currentPageIndex + 1) + query)); writer.Write(HTML4); } } } |
2、在浏览器中查看运行结果如效果图示。
【说明】
1、这段代码的主要功能是呈现一个二行二列的表格,为了显示得清晰一些,设置了这个表格的边框为1,从效果图可以看出,原来DataList显示的内容,显示在表格的第一行,合并单元格后的单元格中,第二行的两个单元格,分别显示“<上一页”和“下一页>”。
2、这两个单元格内的文字,分别加了个链接参数?page={0},page的值暂时由默认的当前页号加1或减1获得。
3、使用类Regex是根据正则表达式“&page=\d+”获取并替代网址中的参数page,具体用法可参阅MSDN的相关内容。
4、if (!DesignMode)是判断当前是否在设计时状态,以决定{}中的语句是否执行。因为{}中的语句需要从地址中获取参数page后面的字符串query,这个query在设计时是未知数,会影响到控件内容的呈现,所以,在设计时不执行,而并不影响实际使用。
三、属性
【效果】
【操作步骤】
1、CustomDataList.cs中为自定义控件添加属性,并改写属性DataSource、函数 OnDataBinding(),完整代码如下:
using System; using System.Collections; using System.Text.RegularExpressions; using System.Web.UI; using System.Web.UI.WebControls; namespace WestGarden.Web { public class CustomDataList : DataList { protected const string HTML1 = "<table border=1><tr><td colspan=2>"; protected const string HTML2 = "</td></tr><tr><td class=paging align=left>"; protected const string HTML3 = "</td><td align=right class=paging>"; 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}>< 上一页</a>"; private const string LINK_MORE = "<a href=?page={0}>下一页 ></a>"; private const string KEY_PAGE = "page"; private const string COMMA = "?"; private const string AMP = "&"; private int pageSize = 10; private int currentPageIndex=0; private int itemCount; private IList dataSource; protected string emptyText; 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; } } override public object DataSource { set { try { dataSource = (IList)value; ItemCount = dataSource.Count; } catch { dataSource = null; ItemCount = 0; } } } override protected void OnDataBinding(EventArgs e) { int start = CurrentPageIndex * pageSize; int size = Math.Min(pageSize, ItemCount - start); IList pageList = new ArrayList(); for (int i = 0; i < size; i++) pageList.Add(dataSource[start + i]); base.DataSource = pageList; base.OnDataBinding(e); } override protected void Render(HtmlTextWriter writer) { if (ItemCount == 0) { writer.Write(emptyText); return; } string query = ""; if (!DesignMode) { query = Context.Request.Url.Query.Replace(COMMA, AMP); query = RX.Replace(query, string.Empty); } writer.Write(HTML1); base.Render(writer); writer.Write(HTML2); writer.Write(string.Format(LINK_PREV, (currentPageIndex - 1) + query)); writer.Write(HTML3); writer.Write(string.Format(LINK_MORE, (currentPageIndex + 1) + query)); writer.Write(HTML4); } } } |
2、Default.aspx中设置CustomDataList1的属性PageSize="4" EmptyText="No Data found.",这两个属性可以属性窗口中设置,如图示:
3、在浏览器中查看运行结果如效果图示。
【说明】
1、CustomDataList分页的主要逻辑是,改写DataList的DataSource属性,在设置数据源CustomDataList1.DataSource =CreateDataSource();时,用Ilist类指针dataSource (也就是俗称的接口)接收过来,同时,获取数据源中数据项的个数ItemCount,然后,改写DataList的DataBind()函数,在函数中通过当前页号CurrentPageIndex(当前页号初始值为0,在后面,点击“上一页”“下一页”时需要重新设置)与属性中设置的每页的数据项个数pageSize获取起始数据项int start = CurrentPageIndex * pageSize;。因为最后一页数据项的个数不一定,所以该页的个数需要重新确定一下int size =Math.Min(pageSize,ItemCount - start);,有了这两个值,就可以把相应的数据项取出存放在重新定义的IList pageList =newArrayList();中,最后,把这个pageList做为DataList的数据源与DataList绑定。
2、在属性窗口中设置属性,需要刷新一下,在工作区窗口中把Default.aspx关闭,再重新打开就可以了。
四、事件
【效果】
【操作步骤】
1、在CustomDataList.cs中,添加事件PageIndexChanged
public event DataGridPageChangedEventHandler PageIndexChanged; virtual protected void OnPageIndexChanged(DataGridPageChangedEventArgs e) { if (PageIndexChanged != null) PageIndexChanged(this, e); } |
2、在Default.aspx中设置事件处理函数onpageindexchanged="CustomDataList1_PageIndexChanged",这个设置也可以在属性窗口中的事件选项卡中进行,如图示:
3、在Default.aspx.cs中添加代码,并删除原来Page_Load()中的代码,完整代码如下:
using System; using System.Data; using System.Collections; using System.Web.UI.WebControls; public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } //创建符合IList接口的表格示例数据 IList CreateDataSource() { DataTable dt = new DataTable(); DataRow dr; dt.Columns.Add(new DataColumn("Number", typeof(Int32))); for (int i = 0; i <= 10; i++) { dr = dt.NewRow(); dr["Number"] = i; dt.Rows.Add(dr); } DataView dv = new DataView(dt); return dv; } protected void CustomDataList1_PageIndexChanged(object source, DataGridPageChangedEventArgs e) { CustomDataList1.CurrentPageIndex = e.NewPageIndex; CustomDataList1.DataSource = CreateDataSource(); CustomDataList1.DataBind(); } } |
4、在浏览器中查看运行结果如效果图示。
【说明】
1、DataGridPageChangedEventHandler是委托,原型为:
publicdelegatevoidDataGridPageChangedEventHandler(Object source,DataGridPageChangedEventArgs e)
C#中的委托,类似于C、C++中的函数指针,从DataGridPageChangedEventHandler的原型可以看出,它相当于是形参为(Object source,DataGridPageChangedEventArgs e),返回值为void的函数指针。只不过在C#中,委托被做成了类,是一种数据类型,需要实例化成类变量,才能存放函数的指针变量。
2、在属性窗口的事件选项卡中设置事件处理程序,需要刷新一下,在工作区窗口中把Default.aspx关闭,再重新打开就可以了。
3、运行结果没有数据显示,是因为事件PageIndexChanged的事件处理程序没有被触发。
五、触发事件处理程序
【效果】
【操作步骤】
1、CustomDataList.cs中改写DataList的OnLoad()函数,并添加函数SetPage(),代码如下:
override protected void OnLoad(EventArgs e) { if (Visible) { string page = Context.Request[KEY_PAGE]; int index = (page != null) ? int.Parse(page) : 0; SetPage(index); } } public void SetPage(int index) { OnPageIndexChanged(new DataGridPageChangedEventArgs(null, index)); } |
3、在浏览器中查看运行结果如效果图示。
【说明】
1、触发(Raise)事件有很多方式,在这里,重载并修改了DataList的OnLaod()函数,在装载自定义控件CustomDataList时,读取地址中的参数page,并根据page的值获取要显示的页号index (如果page为空的话,就设为0),并把index交给函数SetPage(),在SetPage()中调用OnPageIndexChanged(),进而通过PageIndexChanged(this, e);触发了事件处理函数。因为,事件相当于C、C++中的函数指针变量,它的值,在Default.aspx中设置CustomDataList的事件处理函数时,就已经指向了形参为(objectsource,DataGridPageChangedEventArgs e),返回值为void类型的函数了。
2、委托我们使用的是现成的不需要声明的DataGridPageChangedEventHandler,委托要传递的参数变量也是现成的不需要声明的DataGridPageChangedEventArgs,通过newDataGridPageChangedEventArgs(null, index),就可以直接给参数变量赋值并传递了。
3、前面,为了强调显示效果,我们把“<上一页”“下一页>”无条件地显示了,事实上,如果页号为0,就不应该显示当前页“<上一页”,而页号为最后一页的时候,如果显示“下一页>”,由于链接问题,点击会出现错误的。为此,需要在Render(),显示这两句之前分别加上条件:
if (currentPageIndex > 0) writer.Write(string.Format(LINK_PREV, (currentPageIndex - 1) + query)); if (currentPageIndex < PageCount) writer.Write(string.Format(LINK_MORE, (currentPageIndex + 1) + query)); |