Click here to Skip to main content
11,504,892 members (69,693 online)
Click here to Skip to main content

Fast gridview

, 4 Nov 2008 CPOL 27.5K 400 48
Rate this:
Please Sign up or sign in to vote.
A Fast GridView optimized
This is an old version of the currently published article.

Sample Image - maximum width is 600 pixels

Introduction

The GridView control is a very powerful and scalable .net control. You use it inside some application to list entites. However , there is a disadvantage when you want to use the Paging implementation : you must systematically bind the gridview with a select of all data when you change the index to see the other pages. So Imagine that you must work with a datasource with 400 000 records : the postback is expensive , the treatment is not optimized.

Background

I tryed to imagin an implementation solution with a gridview and an independant paging functionnality. The purpose is just to bind the gridview with the current datarows that you see. Sql server allow you to get a part of rows of a query with this syntaxe :

With Prod AS 
( SELECT [ProductID], 
		 [ProductName], 
		 [SupplierID], 
		 [CategoryID], 
		 [QuantityPerUnit], 
		 [UnitPrice], 
		 [UnitsInStock], 
		 [UnitsOnOrder], 
		 [ReorderLevel], 
		 [Discontinued] ,  
		 ROW_NUMBER() OVER (order by ProductName) as RowNumber from Products )
		 SELECT [ProductID], 
		[ProductName], 
		[SupplierID], 
		[CategoryID], 
		[QuantityPerUnit], 
		[UnitPrice], 
		[UnitsInStock], 
		[UnitsOnOrder], 
		[ReorderLevel], 
		[Discontinued] from Prod  Where RowNumber 
Between @pBegin and @pEnd 

I serach on Codeproject if a similar solution existed , and i found a source code whose implemented a very good smart pager. You can find it at : http://www.codeproject.com/KB/aspnet/SmartPager.aspx. So i extend the Gridview class to create GridviewPager class whose own a instance of this SmartPager control. The smart pager control is a complex type property (it has several property) so the pager class must implement IStateManager Interface , and a event to know when the index change

The implementation of the smart pager generate a inconvegnient postback in javascript (method SmartPagerSelectPage of Smartpager.js) when the index change so 2 postback are executed. To resolve this problem i parsed all the request form keys to find CALLBACKPARAM value in order to filter the bad post back , and just executing the good in GridviewPager Page_Load event.

Using the Code

using System;
using System.Net;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using Avg.Controls;
/// <summary>
/// Summary description for GridViewExtender
/// </summary>
/// 
namespace Avg.Controls
{
    public delegate void DataSourceChangedEventHandler(object sender, bool isEmpty);

    [ToolboxData("<{0}:GridViewPager runat="server">")]
public class GridViewPager : GridView
{
    /// <summary>
    /// Handling when the DataSourceChange
    /// </summary>
    public event DataSourceChangedEventHandler DataSourceChanged;

    /// <summary>
    /// A readonly unique ID for the current view.
    /// </summary>
    public string CurrentViewID
    {
        get { return string.Concat("CurrentView_", UniqueID); }
    }

    /// <summary>
    /// Overrides the data source property, so that we get a chance to test whether
    /// the source
    /// being bound contains data or not.
    ///
    /// This is used to communicate with the pager so that for an empty list no
    /// pager is shown.
    /// </summary>
    public override object DataSource
    {
        get
        {
            if (ViewState[CurrentViewID] != null)
                return ViewState[CurrentViewID];
            else
                return base.DataSource;

        }
        set
        {

            base.DataSource = value;
            ViewState[CurrentViewID] = value;


            if (value == null)
            {
                if (DataSourceChanged != null)
                    DataSourceChanged(this, true);
            }
            else
            {
                if (DataSourceChanged != null)
                    DataSourceChanged(this, false);
            }
        }
    }

    /// <summary>
    /// Smart Pager
    /// </summary>
    protected SmartPager pager;

    /// <summary>
    /// Numero of Page cliked
    /// </summary>
    public int PageNumberCliked
    {
        get { return (ViewState["PageNumberCliked"] == null) ? 1 : Int32.Parse(
            ViewState["PageNumberCliked"].ToString()); }
        set { ViewState["PageNumberCliked"] = value; }
    }


    /// <summary>
    /// Display pager numeros grouped by  Display value before '...' buton
    /// </summary>
    public int Display
    {
        get { return pager.Display; }
        set { pager.Display = value; }
    }

    /// <summary>
    /// Current first row to display
    /// </summary>
    public int rowB
    {
        get { return (Page.Session["rowB"] == null) ? 0 : Int32.Parse(
            Page.Session["rowB"].ToString()); }
        set { Page.Session["rowB"] = value; }
    }

    /// <summary>
    /// Current end row to display
    /// </summary>
    public int rowE
    {
        get { return (Page.Session["rowE"] == null) ? PageSize : Int32.Parse(
            Page.Session["rowE"].ToString()); }
        set { Page.Session["rowE"] = value; }
    }

    /// <summary>
    /// Calculate Page Count
    /// </summary>
    public int PageCount
    {
        get
        {

            if (RowCount == 0)
            {
                return PageSize;
            }

            if (this.DataSource == null)
            {
                throw new Exception("Datasource is empy");
            }

            if (PageSize == 0)
            {
                throw new Exception("Page size must be positive");
            }

            return (int)(RowCount / PageSize) + 1;


        }
    }

    /// <summary>
    /// Calculate Row Count
    /// </summary>
    public int RowCount
    {
        get { return (Page.Session["RowCount"] == null) ? 5 : Int32.Parse(
            Page.Session["RowCount"].ToString()); }
        set { Page.Session["RowCount"] = value; }
    }

    /// <summary>
    /// Current Page
    /// </summary>
    public int CurrentPage
    {
        get { return (ViewState["CurrentPage"] == null) ? 1 : Int32.Parse(
            ViewState["CurrentPage"].ToString()); }
        set { ViewState["CurrentPage"] = value; }
    }

    /// <summary>
    /// Constructor
    /// </summary>
public GridViewPager() : base()
{

    pager = new SmartPager();
    pager.OnClickEvent += new OnClickPagerNumberEventHandler(pager_OnClickEvent);

}

#region events to implement on the page side

private static readonly object OnSelectRowEventKey = new object();
/// <summary>
/// This events must be implemented with a select statement to get rows
/// </summary>
public event EventHandler DoSelectRow
{
    add { Events.AddHandler(OnSelectRowEventKey, value); }
    remove { Events.RemoveHandler(OnSelectRowEventKey, value); }
}

protected virtual void OnSelectedRow(EventArgs e)
{
    EventHandler handler = Events[OnSelectRowEventKey] as EventHandler;

    if (handler != null)
        handler(this, e);
    else
        throw new Exception("You must implement OnSelectRow method");
}

private static readonly object OnLoadRowCountEventKey = new object();
/// <summary>
/// This events must be implemented to know the row count
/// </summary>
public event EventHandler DoLoadRowCount
{
    add { Events.AddHandler(OnLoadRowCountEventKey, value); }
    remove { Events.RemoveHandler(OnLoadRowCountEventKey, value); }
}

protected virtual void OnLoadedRowCount(EventArgs e)
{
    EventHandler handler = Events[OnLoadRowCountEventKey] as EventHandler;

    if (handler != null)
        handler(this, e);
    else
        throw new Exception("You must implement OnLoadRowCount method");
}


#endregion events to implement on the page side


#region Component event

/// <summary>
/// OnInit Event
/// </summary>

protected override void OnInit(EventArgs e) 
{ 
    base.OnInit(e); Page.Load += new EventHandler(Page_Load); 
}
/// <summary>
/// PageLoad Event 
/// </summary>
void Page_Load(object sender, EventArgs e) 
{ 
    if (!Page.IsPostBack) OnLoadedRowCount(EventArgs.Empty); 
    bool goodCall = true; //the current page is posted 2 times : 
    // 1- in javascript SmartPager.js by SmartPagerSelectPage when a new page
    // is clicked (with callback parameter) 
    // 2- on the pager_OnClickEvent (without callback parameter) 
    foreach (string Key in Page.Request.Form.AllKeys) 
    {
        if (Key.EndsWith("CALLBACKPARAM")) 
        { 
            goodCall = false; 
        } 
    } 
    //Handle just one time after pager_OnClickEvent call when the CALLBACKPARAM is missing 
    if (goodCall) 
        OnSelectedRow(EventArgs.Empty); 
} 
/// <summary>
/// Load the Control 
/// </summary>
protected override void OnLoad(EventArgs e) 
{ 
    base.OnLoad(e); Controls.Add(pager); 
} 
/// <summary>
/// PreRender the Control 
/// </summary>
protected override void OnPreRender(EventArgs e) 
{ 
    base.OnPreRender(e); 
    pager.PageCount = PageCount; 
}
/// <summary>
/// Event Handler Click Page with ICallBackEventHandler in SmartPager 
/// </summary>
void pager_OnClickEvent(object sender, string pPageNum) 
{
    PageNumberCliked = Int32.Parse(pPageNum); 
    CurrentPage = PageNumberCliked; 
    if (CurrentPage == 1) 
    { 
        rowB = 0; 
        rowE = PageSize; 
    } 
    else 
    { 
        rowE = (PageSize * CurrentPage) - 1;
        rowB = rowE - (PageSize - 1);
    }
    //Call postabck without call back parameters
    Page.Response.Write( "<script language="Javascript">__doPostBack('__Page', 'MyCustomArgument');</script>" ); 
} 
#endregion Component event 
#region IStateManager Members 
/// <summary>
/// Load the ViewState 
/// </summary>
protected override void LoadViewState(object savedState) 
{ 
    if (savedState != null) 
    { 
        object[] state = savedState as object[]; 
        if (state != null && state.Length == 2) 
        { 
            base.LoadViewState(state[0]); 
            if (state[1] != null) 
                ((IStateManager)this.pager).LoadViewState(state[1]); 
        } 
    } 
    else
        base.LoadViewState(savedState); 
} 
/// <summary>
/// Save the ViewState 
/// </summary>
protected override object SaveViewState() 
{ 
    object[] state = new object[2];
    state[0] = base.SaveViewState();
    if (pager != null) 
        state[1] = ((IStateManager)this.pager).SaveViewState(); 
    return state; 
} 
/// <summary>
/// Track the Viewstate 
/// </summary>
protected override void TrackViewState() 
{ 
    base.TrackViewState(); 
    if (pager != null) 
        ((IStateManager)this.pager).TrackViewState(); 
} 
#endregion 
}

Points of Interest

I learned how to extend a complex control like gridview.

History

If you have some improvement , i'm open to listen them

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

David Zenou
Software Developer Several
France France
Fan of .NET Technologies
Go to see my blog : http://davidzenou.blogspot.com/2009/01/david.html

Comments and Discussions


Discussions posted for the Published version of this article. Posting a message here will take you to the publicly available article in order to continue your conversation in public.
 
Generalclick Page Numer "1" and "2" produce same result. what is wrong ? [modified] Pin
vrajaramanv15-Jan-09 17:55
membervrajaramanv15-Jan-09 17:55 
RantThe Download Link also NAVER wrok! Pin
Mr.Crhan29-Oct-08 19:52
memberMr.Crhan29-Oct-08 19:52 
GeneralRe: The Download Link also NAVER wrok! Pin
David Zenou4-Nov-08 2:33
memberDavid Zenou4-Nov-08 2:33 
GeneralRe: The Download Link also NAVER wrok! Pin
Mr.Crhan25-Nov-08 15:32
memberMr.Crhan25-Nov-08 15:32 
GeneralThe download link doesn't work Pin
Scott Bruno29-Oct-08 8:57
memberScott Bruno29-Oct-08 8:57 
GeneralRe: The download link doesn't work Pin
David Zenou4-Nov-08 2:34
memberDavid Zenou4-Nov-08 2:34 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.150520.1 | Last Updated 4 Nov 2008
Article Copyright 2008 by David Zenou
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid