|
|||||||||||||||||||||
|
|||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionIt's not hard to find code examples on how to do custom paging in ASP.NET, but I haven't found any good examples on using a user control to do so. This example shows you how to construct your own user control to perform custom paging; thus, promote code reuse by dropping the control on every page that needs the same functionality. The user control shown in this example uses several events to control the page navigation, if you're not so familiar with how events/delegates work you'll probably have to get some reference on the topic first. This solution also requires writing stored procedures that return pieces of data to be display on each page. I borrowed the idea from Zek3vil in his article - Custom Data Paging in ASP.NET (http://www.codeproject.com/aspnet/custompaging.asp?target=custom%7Cpaging). Basically, he used a To make it simple, I decided to use the employee table in pubs database on MS SQL Server. Let's start by looking at the stored procedure: Creating the Stored ProcedureCREATE 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
The stored procedure has 2 input and 1 output parameter - Next, let's talk about the meat - the user control: Paging User Control<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>
The user control consists of 3 parts - labels that show something like "(Page 1 of 10)," a dropdown listbox that allows you to jump from page to page and 4 VCR-type buttons to navigate between First, Previous, Next and Last page. There are some limitations on ASP.NET image buttons that hopefully will be improved in the next release. First, the image doesn't grey out when the button is disabled. Hence, we'll have to explicitly change the ImageUrl attribute when we enable/disable the button. Second, the mouse cursor remains the same (index finger) regardless of the button state. I didn't do anything to correct this problem in my code but it's a good exercise for you to figure out. Now let's look at the code behind: //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);
Since each VCR-type button and the dropdown listbox has its own event to be linked to, we have to first declare all the public delegates. Notice that we have our own custom event argument type ( Here's the 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; }
}
}
Then, we declare all public events that are hooked up with those delegates: //public events
public event FirstPageEventHandler FirstPage;
public event LastPageEventHandler LastPage;
public event PreviousPageEventHandler PreviousPage;
public event NextPageEventHandler NextPage;
public event PageChangedEventHandler PageChanged;
For those image buttons, we have to forward the 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);
}
Now let's take the 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);
}
We instantiate the protected virtual void OnNextPage(DataNavigatorEventArgs args)
{
if (NextPage != null)
{
// Invoke the delegates.
NextPage(this, args);
}
}
Notice the difference in declaration and signature between Here's the 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;
}
The user control also has public get/set properties that keep track of the current page number, total page number, each image button's state and its image URL. These properties will be used on the ASP.NET page where the control resides. Using the ControlTo use the control, just drag it from the Solution Explorer and drop it on your ASP.NET page. Here's the HTML of the page: <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>
Now let's take a look at the code behind. First, don't forget to hook up all the event handlers for our control: 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);
}
Remember previously we made the 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);
}
}
Here we first increment the current page index then retrieve the data for the DataGrid, finally, we call Note that I use private void Page_Init(object sender, System.EventArgs e)
{
ucDataNavigator.CurrentPage = 1;
}
Inside the if ((totalCount % PAGE_SIZE) == 0)
ucDataNavigator.TotalPages = totalCount/PAGE_SIZE;
else
ucDataNavigator.TotalPages = (totalCount/PAGE_SIZE) + 1;
ConclusionThe biggest advantage of using user controls on your ASP.NET pages is code-reuse. This article shows you how you can utilize this concept to avoid writing repeated code. To make it one step further, instead of a user control, you could write a custom control to do this so that it'd be even easier to reuse your code. Next time I'll show you how to add sorting functionality in our paging user control. Have fun! | ||||||||||||||||||||