Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
West Wind WebSurge - Rest Client and Http Load Testing for Windows

A free standing ASP.NET Pager Web Control


:P
On this page:

Paging in ASP.NET has been relatively easy with stock controls supporting basic paging functionality. However, recently I built an MVC application and one of the things I ran into was that I HAD TO build manual paging support into a few of my pages. Dealing with list controls and rendering markup is easy enough, but doing paging is a little more involved. I ended up with a small but flexible component that can be dropped anywhere. As it turns out the task of creating a semi-generic Pager control for MVC was fairly easily.

Now I’m back to working in Web Forms and thought to myself that the way I created the pager in MVC actually would also work in ASP.NET – in fact quite a bit easier since the whole thing can be conveniently wrapped up into an easily reusable control. A standalone pager would provider easier reuse in various pages and a more consistent pager display regardless of what kind of 'control’ the pager is associated with.

Why a Pager Control?

At first blush it might sound silly to create a new pager control – after all Web Forms has pretty decent paging support, doesn’t it? Well, sort of. Yes the GridView control has automatic paging built in and the ListView control has the related DataPager control.

The built in ASP.NET paging has several issues though:

  • Postback and JavaScript requirements
    If you look at paging links in ASP.NET they are always postback links with javascript:__doPostback() calls that go back to the server. While that works fine and actually has some benefit like the fact that paging saves changes to the page and post them back, it’s not very SEO friendly. Basically if you use javascript based navigation nosearch engine will follow the paging links which effectively cuts off list content on the first page. The DataPager control does support GET based links via the QueryStringParameter property, but the control is effectively tied to the ListView control (which is the only control that implements IPageableItemContainer).
  • DataSource Controls required for Efficient Data Paging Retrieval
    The only way you can get paging to work efficiently where only the few records you display on the page are queried for and retrieved from the database you have to use a DataSource control - only the Linq and Entity DataSource controls  support this natively. While you can retrieve this data yourself manually, there’s no way to just assign the page number and render the pager based on this custom subset. Other than that default paging requires a full resultset for ASP.NET to filter the data and display only a subset which can be very resource intensive and wasteful if you’re dealing with largish resultsets (although I’m a firm believer in returning actually usable sets :-}). If you use your own business layer that doesn’t fit an ObjectDataSource you’re SOL. That’s a real shame too because with LINQ based querying it’s real easy to retrieve a subset of data that is just the data you want to display but the native Pager functionality doesn’t support just setting properties to display just the subset AFAIK.
  • DataPager is not Free Standing
    The DataPager control is the closest thing to a decent Pager implementation that ASP.NET has, but alas it’s not a free standing component – it works off a related control and the only one that it effectively supports from the stock ASP.NET controls is the ListView control. This means you can’t use the same data pager formatting for a grid and a list view or vice versa and you’re always tied to the control.
  • Paging Events
    In order to handle paging you have to deal with paging events. The events fire at specific time instances in the page pipeline and because of this you often have to handle data binding in a way to work around the paging events or else end up double binding your data sources based on paging. Yuk.
  • Styling
    The GridView pager is a royal pain to beat into submission for styled rendering. The DataPager control has many more options and template layout and it renders somewhat cleaner, but it too is not exactly easy to get a decent display for.
  • Not a Generic Solution
    The problem with the ASP.NET controls too is that it’s not generic. GridView, DataGrid use their own internal paging, ListView can use a DataPager and if you want to manually create data layout – well you’re on your own. IOW, depending on what you use you likely have very different looking Paging experiences.

So, I figured I’ve struggled with this once too many and finally sat down and built a Pager control.

The Pager Control

My goal was to create a totally free standing control that has no dependencies on other controls and certainly no requirements for using DataSource controls. The idea is that you should be able to use this pager control without any sort of data requirements at all – you should just be able to set properties and be able to display a pager.

The Pager control I ended up with has the following features:

  • Completely free standing Pager control – no control or data dependencies
  • Complete manual control – Pager can render without any data dependency
  • Easy to use: Only need to set PageSize, ActivePage and TotalItems
  • Supports optional filtering of IQueryable for efficient queries and Pager rendering
  • Supports optional full set filtering of IEnumerable<T> and DataTable
  • Page links are plain HTTP GET href Links
  • Control automatically picks up Page links on the URL and assigns them
    (automatic page detection no page index changing events to hookup)
  • Full CSS Styling support

On the downside there’s no templating support for the control so the layout of the pager is relatively fixed. All elements however are stylable and there are options to control the text, and layout options such as whether to display first and last pages and the previous/next buttons and so on.

To give you an idea what the pager looks like, here are two differently styled examples (all via CSS):

Pagers[4] 

The markup for these two pagers looks like this:

<ww:Pager runat="server" id="ItemPager" 
          PageSize="5" 
          PageLinkCssClass="gridpagerbutton" 
          SelectedPageCssClass="gridpagerbutton-selected"
          PagesTextCssClass="gridpagertext"
          CssClass="gridpager" 
          RenderContainerDiv="true"
          ContainerDivCssClass="gridpagercontainer"
          MaxPagesToDisplay="6" 
          PagesText="Item Pages:" 
          NextText="next"
          PreviousText="previous"
          />
<ww:Pager runat="server" id="ItemPager2" 
          PageSize="5" 
          RenderContainerDiv="true"
          MaxPagesToDisplay="6" />

The latter example uses default style settings so it there’s not much to set. The first example on the other hand explicitly assigns custom styles and overrides a few of the formatting options.

Styling

The styling is based on a number of CSS classes of which the the main pager, pagerbutton and pagerbutton-selected classes are the important ones. Other styles like pagerbutton-next/prev/first/last are based on the pagerbutton style.

The default styling shown for the red outlined pager looks like this:

.pagercontainer
{
    margin: 20px 0;
    background: whitesmoke;    
    padding: 5px;    
}
.pager
{
    float: right;
    font-size: 10pt;
    text-align: left;
}
.pagerbutton,.pagerbutton-selected,.pagertext
{
    display: block;        
    float: left;    
    text-align: center;
    border: solid 2px maroon;        
    min-width: 18px;      
    margin-left: 3px;    
    text-decoration: none;        
    padding: 4px;
}
.pagerbutton-selected
{
    font-size: 130%;
    font-weight: bold;        
    color: maroon;
    border-width: 0px;
    background: khaki;
}
.pagerbutton-first
{
    margin-right: 12px;        
}
.pagerbutton-last,.pagerbutton-prev
{
    margin-left: 12px;        
}
.pagertext
{
    border: none;
    margin-left: 30px;
    font-weight: bold;
}
.pagerbutton a
{
    text-decoration: none;
}
.pagerbutton:hover
{
    background-color: maroon;
    color: cornsilk;
}
.pagerbutton-prev
{
    background-image: url(images/prev.png);
    background-position: 2px center;
    background-repeat: no-repeat;
    width: 35px;
    padding-left: 20px;
}
.pagerbutton-next
{
    background-image: url(images/next.png);
    background-position: 40px center;
    background-repeat: no-repeat;
    width: 35px;
    padding-right: 20px;
    margin-right: 0px;
}

Yup that’s a lot of styling settings although not all of them are required. The key ones are pagerbutton, pager and pager selection. The others (which are implicitly created by the control based on the pagerbutton style) are for custom markup of the ‘special’ buttons.

In my apps I tend to have two kinds of pages: Those that are associated with typical ‘grid’ displays that display purely tabular data and those that have a more looser list like layout. The two pagers shown above represent these two views and the pager and gridpager styles in my standard style sheet reflect these two styles.

Configuring the Pager with Code

Finally lets look at what it takes to hook up the pager. As mentioned in the highlights the Pager control is completely independent of other controls so if you just want to display a pager on its own it’s as simple as dropping the control and assigning the PageSize, ActivePage and either TotalPages or TotalItems.

So for this markup:

<ww:Pager runat="server" id="ItemPagerManual" 
          PageSize="5" 
          MaxPagesToDisplay="6" 
/>

I can use code as simple as:

ItemPagerManual.PageSize = 3;
ItemPagerManual.ActivePage = 4;
ItemPagerManual.TotalItems = 20;

Note that ActivePage is not required - it will automatically use any Page=x query string value and assign it, although you can override it as I did above. TotalItems can be any value that you retrieve from a result set or manually assign as I did above.

A more realistic scenario based on a LINQ to SQL IQueryable result is even easier. In this example, I have a UserControl that contains a ListView control that renders IQueryable data. I use a User Control here because there are different views the user can choose from with each view being a different user control. This incidentally also highlights one of the nice features of the pager: Because the pager is independent of the control I can put the pager on the host page instead of into each of the user controls. IOW, there’s only one Pager control, but there are potentially many user controls/listviews that hold the actual display data.

The following code demonstrates how to use the Pager with an IQueryable that loads only the records it displays: protected voidPage_Load(objectsender, EventArgs e)
{
    Category = Request.Params["Category"] ?? string.Empty;

    IQueryable<wws_Item> ItemList = ItemRepository.GetItemsByCategory(Category);

   
// Update the page and filter the list down
    ItemList = ItemPager.FilterIQueryable<wws_Item>(ItemList);

    // Render user control with a list view     


Control ulItemList = LoadControl("~/usercontrols/" + App.Configuration.ItemListType + ".ascx"); ((IInventoryItemListControl)ulItemList).InventoryItemList = ItemList; phItemList.Controls.Add(ulItemList); // placeholder }

The code uses a business object to retrieve Items by category as an IQueryable which means that the result is only an expression tree that hasn’t execute SQL yet and can be further filtered. I then pass this IQueryable to the FilterIQueryable() helper method of the control which does two main things:

  • Filters the IQueryable to retrieve only the data displayed on the active page
  • Sets the Totaltems property and calculates TotalPages on the Pager

and that’s it! When the Pager renders it uses those values, plus the PageSize and ActivePage properties to render the Pager.

In addition to IQueryable there are also filter methods for IEnumerable<T> and DataTable, but these versions just filter the data by removing rows/items from the entire already retrieved data.

Output Generated and Paging Links

The output generated creates pager links as plain href links. Here’s what the output looks like:

<div id="ItemPager" class="pagercontainer">
    <div class="pager">
        <span class="pagertext">Pages: </span><a href="http://localhost/WestWindWebStore/itemlist.aspx?Page=1" class="pagerbutton" />1</a>
        <a href="http://localhost/WestWindWebStore/itemlist.aspx?Page=2" class="pagerbutton" />2</a>
        <a href="http://localhost/WestWindWebStore/itemlist.aspx?Page=3" class="pagerbutton" />3</a>
        <span class="pagerbutton-selected">4</span>
        <a href="http://localhost/WestWindWebStore/itemlist.aspx?Page=5" class="pagerbutton" />5</a>
        <a href="http://localhost/WestWindWebStore/itemlist.aspx?Page=6" class="pagerbutton" />6</a>
        <a href="http://localhost/WestWindWebStore/itemlist.aspx?Page=20" class="pagerbutton pagerbutton-last" />20</a>&nbsp;<a href="http://localhost/WestWindWebStore/itemlist.aspx?Page=3" class="pagerbutton pagerbutton-prev" />Prev</a>&nbsp;<a href="http://localhost/WestWindWebStore/itemlist.aspx?Page=5" class="pagerbutton pagerbutton-next" />Next</a></div>
        <br clear="all" />
    </div>
</div>

The links point back to the current page and simply append a Page= page link into the page. When the page gets reloaded with the new page number the pager automatically detects the page number and automatically assigns the ActivePage property which results in the appropriate page to be displayed. The code shown in the previous section is all that’s needed to handle paging.

Note that HTTP GET based paging is different than the Postback paging ASP.NET uses by default. Postback paging preserves modified page content when clicking on pager buttons, but this control will simply load a new page – no page preservation at this time.

The advantage of not using Postback paging is that the URLs generated are plain HTML links that a search engine can follow where __doPostback() links are not.

Pager with a Grid

The pager also works in combination with grid controls so it’s easy to bypass the grid control’s paging features if desired. In the following example I use a gridView control and binds it to a DataTable result which is also filterable by the Pager control.

The very basic plain vanilla ASP.NET grid markup looks like this:

   <div style="width: 600px; margin: 0 auto;padding: 20px; ">
        <asp:DataGrid runat="server" AutoGenerateColumns="True" 
                      ID="gdItems" CssClass="blackborder" style="width: 600px;">
        <AlternatingItemStyle CssClass="gridalternate" /> 
        <HeaderStyle CssClass="gridheader" />
        </asp:DataGrid>
        
        <ww:Pager runat="server" ID="Pager" 
              CssClass="gridpager"
              ContainerDivCssClass="gridpagercontainer"
              PageLinkCssClass="gridpagerbutton"
              SelectedPageCssClass="gridpagerbutton-selected"
              PageSize="8" 
              RenderContainerDiv="true"
              MaxPagesToDisplay="6"  />    
    </div>

and looks like this when rendered:
GridPager
using custom set of CSS styles. The code behind for this code is also very simple:

protected void Page_Load(object sender, EventArgs e)
{
    string category = Request.Params["category"] ?? "";

    busItem itemRep = WebStoreFactory.GetItem();
    var items = itemRep.GetItemsByCategory(category)
                       .Select(itm => new {Sku = itm.Sku, Description = itm.Description});           

    // run query into a DataTable for demonstration
    DataTable dt = itemRep.Converter.ToDataTable(items,"TItems");

    // Remove all items not on the current page
    dt = Pager.FilterDataTable(dt,0);
    
    // bind and display
    gdItems.DataSource = dt;
    gdItems.DataBind();
}

A little contrived I suppose since the list could already be bound from the list of elements, but this is to demonstrate that you can also bind against a DataTable if your business layer returns those.

Unfortunately there’s no way to filter a DataReader as it’s a one way forward only reader and the reader is required by the DataSource to perform the bindings.  However, you can still use a DataReader as long as your business logic filters the data prior to rendering and provides a total item count (most likely as a second query).

Control Creation

The control itself is a pretty brute force ASP.NET control. Nothing clever about this other than some basic rendering logic and some simple calculations and update routines to determine which buttons need to be shown. You can take a look at the full code from the West Wind Web Toolkit’s Repository (note there are a few dependencies).

To give you an idea how the control works here is the Render() method:

/// <summary>
/// overridden to handle custom pager rendering for runtime and design time
/// </summary>
/// <param name="writer"></param>
protected override void Render(HtmlTextWriter writer)
{
    base.Render(writer);

    if (TotalPages == 0 && TotalItems > 0)                            
       TotalPages = CalculateTotalPagesFromTotalItems();  

    if (DesignMode)
        TotalPages = 10;

    // don't render pager if there's only one page
    if (TotalPages < 2)
        return;

    if (RenderContainerDiv)
    {
        if (!string.IsNullOrEmpty(ContainerDivCssClass))
            writer.AddAttribute("class", ContainerDivCssClass);
        writer.RenderBeginTag("div");
    }

    // main pager wrapper
    writer.WriteBeginTag("div");
    writer.AddAttribute("id", this.ClientID);
    if (!string.IsNullOrEmpty(CssClass))
        writer.WriteAttribute("class", this.CssClass);
    writer.Write(HtmlTextWriter.TagRightChar + "\r\n");


    // Pages Text
    writer.WriteBeginTag("span");
    if (!string.IsNullOrEmpty(PagesTextCssClass))
        writer.WriteAttribute("class", PagesTextCssClass);
    writer.Write(HtmlTextWriter.TagRightChar);
    writer.Write(this.PagesText);
    writer.WriteEndTag("span");

    // if the base url is empty use the current URL
    FixupBaseUrl();        

    // set _startPage and _endPage
    ConfigurePagesToRender();

    // write out first page link
    if (ShowFirstAndLastPageLinks && _startPage != 1)
    {
        writer.WriteBeginTag("a");
        string pageUrl = StringUtils.SetUrlEncodedKey(BaseUrl, QueryStringPageField, (1).ToString());
        writer.WriteAttribute("href", pageUrl);
        if (!string.IsNullOrEmpty(PageLinkCssClass))
            writer.WriteAttribute("class", PageLinkCssClass + " " + PageLinkCssClass + "-first");
        writer.Write(HtmlTextWriter.SelfClosingTagEnd);
        writer.Write("1");
        writer.WriteEndTag("a");
        writer.Write("&nbsp;");
    }

    // write out all the page links
    for (int i = _startPage; i < _endPage + 1; i++)
    {
        if (i == ActivePage)
        {
            writer.WriteBeginTag("span");
            if (!string.IsNullOrEmpty(SelectedPageCssClass))
                writer.WriteAttribute("class", SelectedPageCssClass);
            writer.Write(HtmlTextWriter.TagRightChar);
            writer.Write(i.ToString());
            writer.WriteEndTag("span");
        }
        else
        {
            writer.WriteBeginTag("a");
            string pageUrl = StringUtils.SetUrlEncodedKey(BaseUrl, QueryStringPageField, i.ToString()).TrimEnd('&');
            writer.WriteAttribute("href", pageUrl);
            if (!string.IsNullOrEmpty(PageLinkCssClass))
                writer.WriteAttribute("class", PageLinkCssClass);
            writer.Write(HtmlTextWriter.SelfClosingTagEnd);
            writer.Write(i.ToString());
            writer.WriteEndTag("a");
        }

        writer.Write("\r\n");
    }

    // write out last page link
    if (ShowFirstAndLastPageLinks && _endPage < TotalPages)
    {
        writer.WriteBeginTag("a");
        string pageUrl = StringUtils.SetUrlEncodedKey(BaseUrl, QueryStringPageField, TotalPages.ToString());
        writer.WriteAttribute("href", pageUrl);
        if (!string.IsNullOrEmpty(PageLinkCssClass))
            writer.WriteAttribute("class", PageLinkCssClass + " " + PageLinkCssClass + "-last");
        writer.Write(HtmlTextWriter.SelfClosingTagEnd);
        writer.Write(TotalPages.ToString());
        writer.WriteEndTag("a");
    }


    // Previous link
    if (ShowPreviousNextLinks && !string.IsNullOrEmpty(PreviousText) && ActivePage > 1)
    {
        writer.Write("&nbsp;");
        writer.WriteBeginTag("a");
        string pageUrl = StringUtils.SetUrlEncodedKey(BaseUrl, QueryStringPageField, (ActivePage - 1).ToString());
        writer.WriteAttribute("href", pageUrl);
        if (!string.IsNullOrEmpty(PageLinkCssClass))
            writer.WriteAttribute("class", PageLinkCssClass + " " + PageLinkCssClass + "-prev");
        writer.Write(HtmlTextWriter.SelfClosingTagEnd);
        writer.Write(PreviousText);
        writer.WriteEndTag("a");
    }

    // Next link
    if (ShowPreviousNextLinks && !string.IsNullOrEmpty(NextText) && ActivePage < TotalPages)
    {
        writer.Write("&nbsp;");
        writer.WriteBeginTag("a");
        string pageUrl = StringUtils.SetUrlEncodedKey(BaseUrl, QueryStringPageField, (ActivePage + 1).ToString());
        writer.WriteAttribute("href", pageUrl);
        if (!string.IsNullOrEmpty(PageLinkCssClass))
            writer.WriteAttribute("class", PageLinkCssClass + " " + PageLinkCssClass + "-next");
        writer.Write(HtmlTextWriter.SelfClosingTagEnd);
        writer.Write(NextText);
        writer.WriteEndTag("a");
    }

    writer.WriteEndTag("div");

    if (RenderContainerDiv)
    {
        if (RenderContainerDivBreak)
writer.Write("<br clear=\"all\" />\r\n"); writer.WriteEndTag("div"); } }

As I said pretty much brute force rendering based on the control’s property settings of which there are quite a few:

PagerVisualStudio

You can also see the pager in the designer above. unfortunately the VS designer (both 2010 and 2008) fails to render the float: left CSS styles properly and starts wrapping after margins are applied in the special buttons. Not a big deal since VS does at least respect the spacing (the floated elements overlay). Then again I’m not using the designer anyway :-}.

Filtering Data

What makes the Pager easy to use is the filter methods built into the control. While this functionality is clearly not the most politically correct design choice as it violates separation of concerns, it’s very useful for typical pager operation. While I actually have filter methods that do something similar in my business layer, having it exposed on the control makes the control a lot more useful for typical databinding scenarios. Of course these methods are optional – if you have a business layer that can provide filtered page queries for you can use that instead and assign the TotalItems property manually.

There are three filter method types available for IQueryable, IEnumerable and for DataTable which tend to be the most common use cases in my apps old and new. The IQueryable version is pretty simple as it can simply rely on on .Skip() and .Take() with LINQ:

 /// <summary>
 /// <summary>
 /// Queries the database for the ActivePage applied manually
 /// or from the Request["page"] variable. This routine
 /// figures out and sets TotalPages, ActivePage and
 /// returns a filtered subset IQueryable that contains
 /// only the items from the ActivePage.
 /// </summary>
 /// <param name="query"></param>
 /// <param name="activePage">
 /// The page you want to display. Sets the ActivePage property when passed. 
 /// Pass 0 or smaller to use ActivePage setting.
 /// </param>
 /// <returns></returns>
 public IQueryable<T> FilterIQueryable<T>(IQueryable<T> query, int activePage)
       where T : class, new()
 {
     ActivePage = activePage < 1 ? ActivePage : activePage;
     if (ActivePage < 1)
         ActivePage = 1;

     TotalItems = query.Count(); 

     if (TotalItems <= PageSize)
     {
         ActivePage = 1;
         TotalPages = 1;
         return query;
     }

     int skip = ActivePage - 1;
     if (skip > 0)
         query = query.Skip(skip * PageSize);

     _TotalPages = CalculateTotalPagesFromTotalItems();

     return query.Take(PageSize);
 }


The IEnumerable<T> version simply  converts the IEnumerable to an IQuerable and calls back into this method for filtering.

The DataTable version requires a little more work to manually parse and filter records (I didn’t want to add the Linq DataSetExtensions assembly just for this):

/// <summary>
/// Filters a data table for an ActivePage.
/// 
/// Note: Modifies the data set permanently by remove DataRows
/// </summary>
/// <param name="dt">Full result DataTable</param>
/// <param name="activePage">Page to display. 0 to use ActivePage property </param>
/// <returns></returns>
public DataTable FilterDataTable(DataTable dt, int activePage)
{
    ActivePage = activePage < 1 ? ActivePage : activePage;
    if (ActivePage < 1)
        ActivePage = 1;

    TotalItems = dt.Rows.Count;

    if (TotalItems <= PageSize)
    {
        ActivePage = 1;
        TotalPages = 1;
        return dt;
    }
    
    int skip = ActivePage - 1;            
    if (skip > 0)
    {
        for (int i = 0; i < skip * PageSize; i++ )
            dt.Rows.RemoveAt(0);
    }
    while(dt.Rows.Count > PageSize)
        dt.Rows.RemoveAt(PageSize);

    return dt;
}

Using the Pager Control

The pager as it is is a first cut I built a couple of weeks ago and since then have been tweaking a little as part of an internal project I’m working on. I’ve replaced a bunch of pagers on various older pages with this pager without any issues and have what now feels like a more consistent user interface where paging looks and feels the same across different controls. As a bonus I’m only loading the data from the database that I need to display a single page. With the preset class tags applied too adding a pager is now as easy as dropping the control and adding the style sheet for styling to be consistent – no fuss, no muss. Schweet.

Hopefully some of you may find this as useful as I have or at least as a baseline to build ontop of…

Resources

Posted in ASP.NET  

The Voices of Reason


 

DotNetKicks.com
December 13, 2009

# Implementing a free standing ASP.NET Pager WebControl

You've been kicked (a good thing) - Trackback from DotNetKicks.com

Rob Conery
December 14, 2009

# re: A free standing ASP.NET Pager Web Control

Hey Rick curious if you tried jQuery's pagination plugin and if so what came up short? I didn't see you mention it above - but it looks like it does what you're doing here straight "out of the box" so to speak:
http://d-scribe.de/webtools/jquery-pagination/demo/demo_options.htm

Rick Strahl
December 14, 2009

# re: A free standing ASP.NET Pager Web Control

@Rob - Ajax paging is cool when it works logistically, but in most cases when you're paging data on a content site you want to have SEO crawlable links. Just looking at some of the demo that you linked and it's doing all client side paging with all data locally loaded.

For list based stuff with Ajax jqGrid works really well and I've used on a few occasions, although it can be a bitch to get the data format right for it.

IAC, slightly different focus. Might be a good idea though to add some client side paging features in the future - wouldn't be difficult as long as the content to be paged is isolated into some sort of server side container.

thoughtcriminal
December 15, 2009

# re: A free standing ASP.NET Pager Web Control

I'm completely with you Rick, I also think that OOTB paging solutions provided by ASP.NET are almost a joke!
Hope guys as MS will get this FIXED in the future.

Dave T
December 15, 2009

# re: A free standing ASP.NET Pager Web Control

Hmm, Everytime I do pagination it needs to now the following
1 total number of records
2 total number of items that are displayed per page
3 current page number.
4 Current Record Filter (IE Seraching for something)
4 Current Record Sort

How would one integrate the last 2 into your control?

Rick Strahl
December 15, 2009

# re: A free standing ASP.NET Pager Web Control

@Dave - the latter two are application concerns not pager concerns. If you have a filter or a sort applied those are applied to the query that returns the result set (my business object call in the example basically).

It's not the pager's job to supply the data. Typically what I do for scenarios like this I have a structure that holds the filter conditions, sort order and anything else related to the query in question and pass that to the business object which processes the query. Alternately you can use parameters but using an object makes it easier to persist these setting in something like Session or ViewState.

Bryan Rite
December 15, 2009

# re: A free standing ASP.NET Pager Web Control

Great article, but I wanted to point out under certain circumstances there is a bug that may greatly affect the usefulness of the datapager.

If you are using a ListView with a ObjectDataSource and a Datapager utilizing the QueryStringParameter there is a bug that causes the ListView to be bound twice - which generally means two trips to the data source! Microsoft has acknowledged but will not backport a fix, will only fix for .Net 4.0:

https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=355348

Rick Strahl
December 15, 2009

# re: A free standing ASP.NET Pager Web Control

@Bryan - thanks for the link. The non-control over the databinding/loading is one of the reasons I don't like the DataPager control - you don't get to control when the datasource access occurs. The whole fact that the DataPager takes over a datasource control that is bound to a separate Web control makes the mind reel and want to yell: WTF were you thinking? :-}

E.R. Gilmore
December 17, 2009

# re: A free standing ASP.NET Pager Web Control

Will this control work in VS2005?

Rick Strahl
December 17, 2009

# re: A free standing ASP.NET Pager Web Control

@ER - it requires .NET 3.5, but should work with VS 2005. Actually the control can probably run in .NET 2.0 but some of the the dependencies in the Web Toolkit assemblies require .NET 3.5.

Rajnish
December 21, 2009

# re: A free standing ASP.NET Pager Web Control

I like this article very much,i want a pager web control for use on my site http://ignou-student.blogspot.com this article will fullfill my requirements.

waqas
December 21, 2009

# re: A free standing ASP.NET Pager Web Control

super super super article.
i was looking for something like this from years.
thanks

Kamran
December 23, 2009

# re: A free standing ASP.NET Pager Web Control

Very nice article Rick

Noel
January 06, 2010

# re: A free standing ASP.NET Pager Web Control

I love the FilterIQueryable! It really helps streamline how the app deals with a pager control. Very thoughtful & nice addition.

Walt
May 30, 2010

# re: A free standing ASP.NET Pager Web Control

Looks interesting, but not entirely free-standing. There's a dependency on "StringUtils", which is one of your custom classes. Too bad, wanted to try it out ...

m
April 24, 2012

# re: A free standing ASP.NET Pager Web Control

Maybe you didn't noticed, but there is a problem with the rendering of the pager control which causes it to look bad:

<div class="pagercontainer">

<div class="pager"<span class= ...

notice the missing ">" character after the second div.

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024