自定义分页(使用用户控件)






4.32/5 (18投票s)
2004年3月17日
5分钟阅读

164126

2686
通过使用用户控件执行自定义分页来促进代码重用
引言
在ASP.NET中实现自定义分页的代码示例并不难找,但我没有找到任何关于使用用户控件来实现自定义分页的好例子。本示例将向您展示如何构建自己的用户控件来实现自定义分页;从而,通过将该控件放置在需要相同功能的每个页面上来促进代码重用。
本示例中的用户控件使用多个事件来控制页面导航,如果您对事件/委托的工作原理不太熟悉,可能需要先查阅相关资料。
此解决方案还需要编写存储过程来返回要显示在每个页面上的数据片段。我借鉴了Zek3vil在其文章《ASP.NET中的自定义数据分页》(https://codeproject.org.cn/aspnet/custompaging.asp?target=custom%7Cpaging)中的想法。基本上,他使用了一个带有ID列的temp
表,该列具有IDENTITY
和PRIMARY KEY
属性,用于存储返回的数据。尽管该示例是用T-SQL编写的,但同样的概念也可以应用于PL/SQL。
为了简单起见,我决定使用MS SQL Server的pubs数据库中的employee表。让我们从存储过程开始。
创建存储过程
CREATE PROCEDURE Get_Employees( @CurrentPage int,
@PageSize int,
@TotalRecords int OUTPUT)
AS
-- Turn off count return.
Set NoCount On
-- Declare variables.
Declare @FirstRec int
Declare @LastRec int
-- Initialize variables.
Set @FirstRec = (@CurrentPage - 1) * @PageSize
Set @LastRec = (@CurrentPage * @PageSize + 1)
-- Create a temp table to hold the current page of data
-- Add an ID column to count the records
Create Table #TempTable
(
EmpId int IDENTITY PRIMARY KEY,
fname varchar(20),
lname varchar(30),
pub_id char(4),
hire_date datetime
)
--Fill the temp table with the reminders
Insert Into #TempTable
(
fname,
lname,
pub_id,
hire_date
)
Select fname,
lname,
pub_id,
hire_date
From employee
Order By lname
--Select one page of data based on the record numbers above
Select fname,
lname,
pub_id,
hire_date
From #TempTable
Where EmpId > @FirstRec
And EmpId < @LastRec
--Return the total number of records available as an output parameter
Select @TotalRecords = Count(*)
From #TempTable
GO
该存储过程有两个输入参数和一个输出参数:@CurrentPage
是当前页码,应大于0;@PageSize
决定每页显示多少条记录;@TotalRecords
返回总记录数,这也用于计算总页数。
接下来,我们来谈谈核心部分——用户控件。
分页用户控件
<table width="100%">
<tr>
<td>(Page
<asp:label id="lblCurrentPage" Runat="server"></asp:label>of
<asp:label id="lblTotalPages" Runat="server"></asp:label>)
</td>
<td valign="top" align="right">
Page
<asp:DropDownList id="ddPage" runat="server"
AutoPostBack="true"></asp:DropDownList>
</td>
<td align="right">
<asp:imagebutton id="btnFirst" Runat="server" Enabled="false"
ImageUrl="Images/NavFirstPageDisabled.gif" />
<asp::imagebutton id="btnPrevious" Runat="server" Enabled="false"
ImageUrl="Images/NavPreviousPageDisabled.gif" />
<asp:imagebutton id="btnNext" Runat="server" Enabled="false"
ImageUrl="Images/NavNextPageDisabled.gif" />
<asp:imagebutton id="btnLast" Runat="server" Enabled="false"
ImageUrl="Images/NavLastPageDisabled.gif" />
</td>
</tr>
</table>
该用户控件包含3个部分:显示“(第1页,共10页)”等信息的标签,允许您跳转页面的下拉列表框,以及用于导航到第一页、上一页、下一页和最后一页的4个VCR式按钮。
ASP.NET图像按钮有一些限制,希望在下一个版本中能得到改进。首先,当按钮禁用时,图像不会变灰。因此,我们必须在启用/禁用按钮时显式更改ImageUrl属性。其次,鼠标光标在任何按钮状态下都保持不变(食指)。我的代码中没有做任何处理来纠正这个问题,但这是一个很好的练习,供您自行解决。
现在我们来看代码隐藏。
//public deletgates
public delegate void FirstPageEventHandler(object sender,
DataNavigatorEventArgs e);
public delegate void LastPageEventHandler(object sender,
DataNavigatorEventArgs e);
public delegate void PreviousPageEventHandler(object sender,
DataNavigatorEventArgs e);
public delegate void NextPageEventHandler(object sender,
DataNavigatorEventArgs e);
public delegate void PageChangedEventHandler(object sender,
DataNavigatorEventArgs e);
由于每个VCR式按钮和下拉列表框都有自己的事件需要链接,我们必须首先声明所有公共委托。请注意,我们有自己的自定义事件参数类型(DataNavigatorEventArgs
),因为我们需要在每次页面导航时跟踪当前页码和总页数。
这是DataNavigatorEventArgs
类。
public class DataNavigatorEventArgs : EventArgs
{
private int m_iCurrentPage;
private int m_iTotalPages;
public DataNavigatorEventArgs()
{
}
public int CurrentPage
{
get { return m_iCurrentPage; }
set { m_iCurrentPage = value; }
}
public int TotalPages
{
get { return m_iTotalPages; }
set { m_iTotalPages = value; }
}
}
然后,我们声明所有与这些委托关联的公共事件。
//public events
public event FirstPageEventHandler FirstPage;
public event LastPageEventHandler LastPage;
public event PreviousPageEventHandler PreviousPage;
public event NextPageEventHandler NextPage;
public event PageChangedEventHandler PageChanged;
对于那些图像按钮,我们必须将Click
事件转发给已声明的委托。我们在InitializeComponent()
函数中使用以下代码来实现这一点。
private void InitializeComponent()
{
this.btnPrevious.Click += new System.Web.UI.ImageClickEventHandler
(this.OnPreviousPageButton);
this.btnNext.Click += new System.Web.UI.ImageClickEventHandler
(this.OnNextPageButton);
this.btnFirst.Click += new System.Web.UI.ImageClickEventHandler
(this.OnFirstPageButton);
this.btnLast.Click += new System.Web.UI.ImageClickEventHandler
(this.OnLastPageButton);
this.ddPage.SelectedIndexChanged += new EventHandler
(this.OnPageChangedButton);
this.Load += new System.EventHandler(this.Page_Load);
}
现在以Next
按钮为例,我们将Click
事件转发给this.OnNextPageButton()
函数。该函数如下所示:
protected void OnNextPageButton(object sender, ImageClickEventArgs e)
{
DataNavigatorEventArgs args = new DataNavigatorEventArgs();
args.CurrentPage = int.Parse(lblCurrentPage.Text);
args.TotalPages = int.Parse(lblTotalPages.Text);
SetDropDownPageNumber(args.CurrentPage + 1);
OnNextPage(args);
}
我们实例化DataNavigatorEventArgs
对象,从用户控件上的标签为其赋值,调用SetDropDownPageNumber()
来设置下拉列表框中显示的正确页码,最重要的是,将调用转发给OnNextPage()
以通过调用委托来引发事件。
protected virtual void OnNextPage(DataNavigatorEventArgs args)
{
if (NextPage != null)
{
// Invoke the delegates.
NextPage(this, args);
}
}
请注意OnNextPageButton()
和OnNextPage()
之间的声明和签名差异。(注意:引发事件的函数的名称必须是OnEventName,例如OnNextPage
)。
这是设置下拉列表框页码的SetDropDownPageNumber()
函数。
private void SetDropDownPageNumber(int iCurrentPage)
{
if (ddPage.Items.Count > 0)
// since SelectedIndex is 0-based, we have to
// take the current page number and minus 1
ddPage.SelectedIndex = iCurrentPage - 1;
}
用户控件还具有公共的get/set属性,用于跟踪当前页码、总页数、每个图像按钮的状态及其图像URL。这些属性将在控件所在的ASP.NET页面中使用。
使用控件
要使用该控件,只需将其从解决方案资源管理器拖放到ASP.NET页面上。这是页面的HTML。
<form id="Form1" method="post" runat="server">
<table width="100%">
<tr>
<td>
<asp:datagrid id="dgEmp" runat="server"
EnableViewState="false"
AlternatingItemStyle-BackColor="Silver"
AllowCustomPaging="true"
Width="100%" HeaderStyle-BackColor="#6633ff"
HeaderStyle-ForeColor="#ffffff"
HeaderStyle-Font-Bold="true"></asp:datagrid>
</td>
</tr>
<tr>
<td><uc1:datanavigator id="ucDataNavigator"
runat="server"></uc1:datanavigator>
</td>
</tr>
</table>
</form>
现在我们来看代码隐藏。首先,别忘了为我们的控件挂接所有事件处理程序。
private void InitializeComponent()
{
this.ucDataNavigator.FirstPage += new FirstPageEventHandler
(this.FirstPage);
this.ucDataNavigator.PreviousPage += new PreviousPageEventHandler
(this.PreviousPage);
this.ucDataNavigator.NextPage += new NextPageEventHandler
(this.NextPage);
this.ucDataNavigator.LastPage += new LastPageEventHandler
(this.LastPage);
this.ucDataNavigator.PageChanged += new PageChangedEventHandler
(this.PageChanged);
this.Load += new System.EventHandler(this.Page_Load);
this.Init += new EventHandler(this.Page_Init);
}
还记得我们之前让Next
图像按钮的Click
事件引发NextPage
事件吗?在这里,我们定义我们的NextPage
事件处理程序。
protected void NextPage(object sender, DataNavigatorEventArgs e)
{
if (e.CurrentPage <= e.TotalPages)
{
// Increment the current page index.
ucDataNavigator.CurrentPage++;
// Get the data for the DataGrid.
BindGrid();
EnableDisableButtons(e.TotalPages);
}
}
在这里,我们首先增加当前页索引,然后检索DataGrid的数据,最后调用EnableDisableButtons()
来控制每个VCR式图像按钮的状态和图像。
请注意,我在上面的Page_Init()
方法中将用户控件的CurrentPage
属性初始化为1,因为这只需要执行一次。
private void Page_Init(object sender, System.EventArgs e)
{
ucDataNavigator.CurrentPage = 1;
}
在BindGrid()
方法中,我使用了以下逻辑来计算页数:
if ((totalCount % PAGE_SIZE) == 0)
ucDataNavigator.TotalPages = totalCount/PAGE_SIZE;
else
ucDataNavigator.TotalPages = (totalCount/PAGE_SIZE) + 1;
totalCount
的值来自存储过程的输出参数,而PAGE_SIZE
是我代码中定义的一个常量,值为10。所以,另一个很好的练习是外部化PAGE_SIZE
常量;也就是说,不要硬编码它,而是让它变成表驱动的,或者将值存储在配置文件中。
结论
在ASP.NET页面上使用用户控件的最大优点是代码重用。本文展示了如何利用这个概念来避免编写重复的代码。更进一步,您可以使用自定义控件来实现这一点,这样可以更轻松地重用代码。
下次我将向您展示如何在分页用户控件中添加排序功能。玩得开心!