GridView with custom Digg-like paging

Aug 27
2007 07:42 (ASP.NET, Development) · Русский (18,445 views)

GridView is a great highly customizable ASP.NET control. Today I want to show, how to create derived control, which allows to add Digg-style pagination to your application.

First, we will create derived control and add property UseCustomPager, which will define whether or not to use Digg-style pagination:

using System;
using System.Globalization;
using System.Reflection;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;

namespace App_Code
{
    public class GridViewWithPager : GridView
    {
        public bool UseCustomPager
        {
            get { return (bool?) ViewState["UseCustomPager"] ?? false; }
            set { ViewState["UseCustomPager"] = value; }
        }
    }
}

GridView has virtual method InitializePager which could be overridden to create our custom pager:

protected override void InitializePager(GridViewRow row, int columnSpan, PagedDataSource pagedDataSource)
{
    if (UseCustomPager)
        CreateCustomPager(row, columnSpan, pagedDataSource);
    else
        base.InitializePager(row, columnSpan, pagedDataSource);
}

Now let’s create our custom pager:

protected virtual void CreateCustomPager(GridViewRow row, int columnSpan, PagedDataSource pagedDataSource)
{
    int pageCount = pagedDataSource.PageCount;
    int pageIndex = pagedDataSource.CurrentPageIndex + 1;
    int pageButtonCount = PagerSettings.PageButtonCount;

    TableCell cell = new TableCell();
    row.Cells.Add(cell);
    if (columnSpan > 1) cell.ColumnSpan = columnSpan;

    if (pageCount > 1)
    {
        HtmlGenericControl pager = new HtmlGenericControl("div");
        pager.Attributes["class"] = "pagination";
        cell.Controls.Add(pager);

        int min = pageIndex - pageButtonCount;
        int max = pageIndex + pageButtonCount;

        if (max > pageCount)
            min -= max - pageCount;
        else if (min < 1)
            max += 1 - min;

        // Create "previous" button
        Control page = pageIndex > 1
                        ? BuildLinkButton(pageIndex - 2, PagerSettings.PreviousPageText, "Page", "Prev")
                        : BuildSpan(PagerSettings.PreviousPageText, "disabled");
        pager.Controls.Add(page);

        // Create page buttons
        bool needDiv = false;
        for (int i = 1; i <= pageCount; i++)
        {
            if (i <= 2 || i > pageCount - 2 || (min <= i && i <= max))
            {
                string text = i.ToString(NumberFormatInfo.InvariantInfo);
                page = i == pageIndex
                        ? BuildSpan(text, "current")
                        : BuildLinkButton(i - 1, text, "Page", text);
                pager.Controls.Add(page);
                needDiv = true;
            }
            else if (needDiv)
            {
                page = BuildSpan("&hellip;", null);
                pager.Controls.Add(page);
                needDiv = false;
            }
        }

        // Create "next" button
        page = pageIndex < pageCount
                ? BuildLinkButton(pageIndex, PagerSettings.NextPageText, "Page", "Next")
                : BuildSpan(PagerSettings.NextPageText, "disabled");
        pager.Controls.Add(page);
    }
}

private Control BuildLinkButton(int pageIndex, string text, string commandName, string commandArgument)
{
    PagerLinkButton link = new PagerLinkButton(this);
    link.Text = text;
    link.EnableCallback(ParentBuildCallbackArgument(pageIndex));
    link.CommandName = commandName;
    link.CommandArgument = commandArgument;
    return link;
}

private Control BuildSpan(string text, string cssClass)
{
    HtmlGenericControl span = new HtmlGenericControl("span");
    if (!String.IsNullOrEmpty(cssClass)) span.Attributes["class"] = cssClass;
    span.InnerHtml = text;
    return span;
}

Several pager settings used in this code:

  • PagerSettings.PreviousPageText — text to be shown on the “Previous” button.
  • PagerSettings.NextPageText — text to be shown on the “Next” button.
  • PagerSettings.PageButtonCount — how many pages to show before and after the current page.

You could see, that in BuildLinkButton method I have used custom control PagerLinkButton. This is just descendant of the LinkButton control which simplifies usage inside our GridViewWithPager:

using System;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace App_Code
{
    public class PagerLinkButton : LinkButton
    {
        public PagerLinkButton(IPostBackContainer container)
        {
            _container = container;
        }

        public void EnableCallback(string argument)
        {
            _enableCallback = true;
            _callbackArgument = argument;
        }

        public override bool CausesValidation
        {
            get { return false; }
            set { throw new ApplicationException("Cannot set validation on pager buttons"); }
        }

        protected override void Render(HtmlTextWriter writer)
        {
            SetCallbackProperties();
            base.Render(writer);
        }

        private void SetCallbackProperties()
        {
            if (_enableCallback)
            {
                ICallbackContainer container = _container as ICallbackContainer;
                if (container != null)
                {
                    string callbackScript = container.GetCallbackScript(this, _callbackArgument);
                    if (!string.IsNullOrEmpty(callbackScript)) OnClientClick = callbackScript;
                }
            }
        }

        #region Private fields

        private readonly IPostBackContainer _container;
        private bool _enableCallback;
        private string _callbackArgument;

        #endregion
    }
}

Our control is almost done. All we need is to define method ParentBuildCallbackArgument. As you could see from GridView sources, this method is used for serializing page index, sort direction and sort expression, but for some reason, it has been defined as internal. I don’t like hacks, but in this case I think that I can cheat a little:

private string ParentBuildCallbackArgument(int pageIndex)
{
    MethodInfo m =
        typeof (GridView).GetMethod("BuildCallbackArgument", BindingFlags.NonPublic | BindingFlags.Instance, null,
                                    new Type[] {typeof (int)}, null);
    return (string) m.Invoke(this, new object[] {pageIndex});
}

BTW, as you could see, I have not added any comments to methods. It is done specially, because I don’t want to create custom controls library, just sharing my experience :-)

And now I’ll show you example of usage:

<asp:XmlDataSource runat="server" ID="xdsCountries"
    DataFile="~/App_Data/CountryCodeList.xml" />


<ac:GridViewWithPager runat="server" UseCustomPager="true" AllowPaging="true"
    DataSourceID="xdsCountries" PageSize="10" AutoGenerateColumns="false">

    <PagerSettings PreviousPageText="&laquo; previous"
        NextPageText="next &raquo;" PageButtonCount="3" />

    <Columns>
        <asp:TemplateField HeaderText="Code">
            <ItemTemplate><%# XPath("CountryCoded") %></ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Name">
            <ItemTemplate><%# XPath("CountryName") %></ItemTemplate>
        </asp:TemplateField>
    </Columns>
</ac:GridViewWithPager>

And screenshot:

Digg-style pagination

Full source code could be downloaded here.

12 Responses to 'GridView with custom Digg-like paging'

Subscribe to comments with RSS or TrackBack to 'GridView with custom Digg-like paging'.

1
Kigorw
said on 2007-08-29 at 11.57 am

GridView — это отличный сильно кастомизируемый контрол ASP.NET. - Ты серьезно так считаешь?

2
said on 2007-08-29 at 12.27 pm

Да, и не думаю, что я сильно ошибаюсь :-) Все, что нужно - легко получить. Включая DIV’ную разметку вместо табличной, которую он генерит по умолчанию, всевозможные сортировки и пейджинги, управление блоками данных (колонками в частности). Да, это действительно отлично кастомизируемый контрол. Если есть пример того, что сложно сделать — велкам.

3
Kigorw
said on 2007-08-29 at 1.40 pm

Вполне допускаю что я его криво использую.

1. Была задача реализовать фильтры, встроенные в каждую колонку. Реализовал, но криво, джаваскриптом создаю строку пустую и туда засовываю фильтры.

2. Проблема в датабиндинге. Связал с обжект датасоурсом. При любом действии (то же нажатие на кнопку пейджинга) вызывается много раз DataBind, что ведет к ненужным запросом данных…

Мысль в том что не вижу смысла бороться с этой всей кастомизацией, делая элементарные действия. Не хочу думать про весь этот цикл событий жизни, вспоминая что исполнится первыми и где тут еще поставить очередной DataBind, чтоб все отрисовалось правильно…

4
Total Beaver
said on 2007-09-19 at 10.46 pm

Standard GridView is sux with such paging style or another.

5
said on 2007-09-19 at 11.23 pm

2 Total Beaver: Any arguments?

6
said on 2007-09-26 at 4.52 pm

Very nice one I implemented it in one of my projects and looks + works perfectly fine.

Thanks a bunch

7
said on 2007-09-26 at 11.31 pm

great code but does someone has the VB.net translation of this, im not into C#

thx
Wouter

8
Mel User
said on 2008-04-29 at 9.48 am

All the functionalities works except when I tries to get the First and Last page index, it always return 0. Any idea why?

9
Andrew
said on 2008-08-11 at 8.32 pm

Hi!

Thanks for a great control. I am having a problem capturing the postback on the page click event for some reason.

I am running this with VS 2008 and ASP.NET 3.5. Could that be the problem?

Any help would be greatly appreciated.

Cheers

10
said on 2008-08-19 at 7.22 pm

Hey Wouter, you can use the following website to convert the code to vb.net. That’s What i did and it works perfectly

http://labs.developerfusion.co.uk/convert/csharp-to-vb.aspx

11
krishna
said on 2008-09-04 at 12.36 pm

All the functionalities works except when I tries to get the First and Last page index, it always return 0. Any idea why?

12
steve martin
said on 2008-09-29 at 5.11 pm

Thanks for sharing your work. This saves me a whole lot of time to get something that looks really good.

Post a comment

You can use simple HTML-formatting tags (like <a>, <ul> and others). To format your code sample use <code lang="php">$a = "hello";</code> (allowed languages are ruby, php, yaml, html, csharp, javascript). Also you could use <code>$a = "hello";</code> and its syntax would not be highlighted. If you are not using <code> tag, replace < sign with &lt;.

Submit Comment

 
Copyright © 2005 - 2008, Dmytro Shteflyuk