Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Custom Paging with User Control

0.00/5 (No votes)
16 Mar 2004 1  
Promote Code Reuse by Using a User Control to Do Custom Paging

Sample Image - PagingUserControl.jpg

Introduction

It'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 temp table with an ID column that has the IDENTITY and PRIMARY KEY attribute to hold the returned data. Although the example was written in T-SQL, the same concept could be implemented in PL/SQL as well.

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 Procedure

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

The stored procedure has 2 input and 1 output parameter - @CurrentPage is the current page number and should be greater than 0; @PageSize determines how many records to display on each page; @TotalRecords returns the number of records in total, which is also being used to calculate the total number of pages.

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 (DataNavigatorEventArgs) because we need to keep track of the current page and total page number each time we navigate between pages.

Here's the DataNavigatorEventArgs class:

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 Click event to our declared delegates. We do this by the following code inside the InitializeComponent() function:

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 Next button for example, we forward the Click event to this.OnNextPageButton() function. Here's how that function looks like:

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 DataNavigatorEventArgs object, assign its values from the labels on the user control, call the SetDropDownPageNumber() to set the correct page number displayed in the dropdown listbox, and most importantly, forward the call to OnNextPage() to raise the event by invoking delegates.

protected virtual void OnNextPage(DataNavigatorEventArgs args)

{

    if (NextPage != null)

    {

        // Invoke the delegates.


        NextPage(this, args);

    }

}

Notice the difference in declaration and signature between OnNextPageButton() and OnNextPage(). (Note: the name of your function that raises the event has to be OnEventName such as OnNextPage).

Here's the SetDropDownPageNumber() function that sets the page number for the dropdown listbox:

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 Control

To 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 Click event of the Next image button raise the NextPage event? Here we get to define our NextPage event handler:

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 EnableDisableButtons() to control the state and image of each VCR-type image button.

Note that I use Page_Init() method above to intialize the CurrentPage property on the user control to 1 since this only has to be done once.

private void Page_Init(object sender, System.EventArgs e)

{

    ucDataNavigator.CurrentPage = 1;

}

Inside the BindGrid() method I used the following logic to calculate the number of pages:

if ((totalCount % PAGE_SIZE) == 0)

    ucDataNavigator.TotalPages = totalCount/PAGE_SIZE;

else

    ucDataNavigator.TotalPages = (totalCount/PAGE_SIZE) + 1;

totalCount's value is from the output parameter of the stored procedure and PAGE_SIZE is a constant defined as 10 in my code. So another good exercise for you to do is to externalize the PAGE_SIZE constant; i.e., instead of hard-coding it, make it table-driven or store the value in a configuration file.

Conclusion

The 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!

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here