控件也是类——分步实现具有分页功能的自定义DataList控件【附源代码】

一、控件也是类

【效果】

【操作步骤】

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接口的简单的表格数据。

3DataList的模板和Repeater差不多,还是手工做,方便一些。和Repeater相比,主要多了一个RepeatColumns属性,在电子商务系统中,用来展示商品列表比较方便,但默认没有分页功能,可以通过本例,自定义实现。

 

二、呈现

【效果】

【操作步骤】

1CustomDataList.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的相关内容。

4if (!DesignMode)是判断当前是否在设计时状态,以决定{}中的语句是否执行。因为{}中的语句需要从地址中获取参数page后面的字符串query,这个query在设计时是未知数,会影响到控件内容的呈现,所以,在设计时不执行,而并不影响实际使用。

三、分页显示

【效果】

 

【操作步骤】

1CustomDataList.cs中为自定义控件添加属性,并改写,完整代码如下:

 

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、在浏览器中查看运行结果如效果图示。

 

【说明】

1CustomDataList分页的主要逻辑是,改写DataListDataSource属性,在设置数据源CustomDataList1.DataSource =CreateDataSource();时,用Ilist类指针dataSource (也就是俗称的接口)接收过来,同时,获取数据源中数据项的个数ItemCount,然后,改写DataListDataBind()函数,在函数中通过当前页号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、在浏览器中查看运行结果如效果图示。

【说明】

1DataGridPageChangedEventHandler是委托,原型为:

publicdelegatevoidDataGridPageChangedEventHandler(Object source,DataGridPageChangedEventArgs e)

 

C#中的委托,类似于CC++中的函数指针,从DataGridPageChangedEventHandler的原型可以看出,它相当于是形参为(Object source,DataGridPageChangedEventArgs e),返回值为void的函数指针。只不过在C#中,委托被做成了类,是一种数据类型,需要实例化成类变量,才能存放函数的指针变量。

2、在属性窗口的事件选项卡中设置事件处理程序,需要刷新一下,在工作区窗口中把Default.aspx关闭,再重新打开就可以了。

3、运行结果没有数据显示,是因为事件PageIndexChanged的事件处理程序没有被触发。

 

五、触发事件处理程序

【效果】

 

【操作步骤】

1CustomDataList.cs中改写DataListOnLoad()函数,并添加函数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)事件有很多方式,在这里,重载并修改了DataListOnLaod()函数,在装载自定义控件CustomDataList时,读取地址中的参数page,并根据page的值获取要显示的页号index (如果page为空的话,就设为0),并把index交给函数SetPage(),在SetPage()中调用OnPageIndexChanged(),进而通过PageIndexChanged(this, e);触发了事件处理函数。因为,事件相当于CC++中的函数指针变量,它的值,在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));

  


 源代码:http://download.csdn.net/detail/yousuosi/4669295

版权所有©2012,WestGarden.欢迎转载,转载请注明出处.更多文章请参阅博客http://WestGarden.cnblogs.com/

 

posted on 2012-10-21 02:07  WestGarden  阅读(1347)  评论(3编辑  收藏  举报

导航