Using SPGridView With DataPager (For Nicer Pagination)

14 September 2012

Surprise, surprise - another post about SPGridView. This time, we're looking at how to use the nice .NET 3.5 DataPager control with SPGridView. Out of the box, they don't work together (for the same reason GridView and DataPager don't), because SPGridView doesn't implement the IPageableItemContainer interface, which the DataPager expects.

Lucky for us, someone's already done all the hard work and thinking in how to extend GridView to implement this interface; the best post I found was this one by Matt which explains the required steps and rationale behind the code very well - I won't repeat it here.

Unfortunately for us though, the link to download the sample code is broken - but with the magic of the internet, I present to you the code below.

public class CustomSPGridView : SPGridView, IPageableItemContainer
{
    //extending SPGridView to support DataPager controls. see 
    //http://mattberseth.com/blog/2008/04/using_a_datapager_with_the_gri.html

    int IPageableItemContainer.MaximumRows
    {
        get { return this.PageSize; }
    }

    void IPageableItemContainer.SetPageProperties(int startRowIndex, int maximumRows, bool databind)
    {
        int newPageIndex = (startRowIndex / maximumRows);

        this.PageSize = maximumRows; if (this.PageIndex != newPageIndex)
        {
            bool isCanceled = false;

            if (databind)
            {
                // create the event args and raise the event

                GridViewPageEventArgs args = new GridViewPageEventArgs(newPageIndex); this.OnPageIndexChanging(args);
                isCanceled = args.Cancel;

                newPageIndex = args.NewPageIndex;
            }

            // if the event wasn't cancelled
            // go ahead and change the paging values
            if (!isCanceled)
            {
                this.PageIndex = newPageIndex; if (databind)
                {
                    this.OnPageIndexChanged(EventArgs.Empty);
                }
            }

            if (databind)
            {
                this.RequiresDataBinding = true;
            }
        }
    }

    private static readonly object EventTotalRowCountAvailable = new object();

    int IPageableItemContainer.StartRowIndex
    {
        get { return this.PageSize * this.PageIndex; }
    }

    event EventHandler<PageEventArgs> IPageableItemContainer.TotalRowCountAvailable
    {
        add { base.Events.AddHandler(EventTotalRowCountAvailable, value); }
        remove { base.Events.RemoveHandler(EventTotalRowCountAvailable, value); }
    }

    protected override int CreateChildControls(IEnumerable dataSource, bool dataBinding)
    {
        int rows = base.CreateChildControls(dataSource, dataBinding);
        // if the paging feature is enabled, determine
        // the total number of rows in the datasource

        if (this.AllowPaging)
        {
            // if we are databinding, use the number of rows that were created,
            // otherwise cast the datasource to an Collection and use that as the count

            int totalRowCount = dataBinding ? rows : ((ICollection)dataSource).Count;
            // raise the row count available event

            IPageableItemContainer pageableItemContainer = this as IPageableItemContainer;
            this.OnTotalRowCountAvailable(new PageEventArgs(pageableItemContainer.StartRowIndex,pageableItemContainer.MaximumRows,totalRowCount));

            // make sure the top and bottom pager rows are not visible
            if (this.TopPagerRow != null)
            {
                this.TopPagerRow.Visible = false;
            }

            if (this.BottomPagerRow != null)
            {
                this.BottomPagerRow.Visible = false;
            }
        }

        return rows;
    }

    protected virtual void OnTotalRowCountAvailable(PageEventArgs e)
    {
        EventHandler<PageEventArgs> handler = (EventHandler<PageEventArgs>)base.Events[EventTotalRowCountAvailable]; if (handler != null)
        {
            handler(this, e);
        }
    }
}

With this extended version of a SPGridView, you should now be able to have all of that DataPager / templated goodness.

In case you need it in VB, the code was graciously provided by TheMeanFiddler / Jason Basin below:

Public Class CustomSPGridView
    Inherits GridView
    Implements IPageableItemContainer

    'extending SPGridView to support DataPager controls. see     
    'http://mattberseth.com/blog/2008/04/using_a_datapager_with_the_gri.html    
    Private ReadOnly Property IPageableItemContainer_MaximumRows() As Integer Implements IPageableItemContainer.MaximumRows
        Get
            Return Me.PageSize
        End Get
    End Property
    Private Sub IPageableItemContainer_SetPageProperties(ByVal startRowIndex As Integer, ByVal maximumRows As Integer, ByVal databind As Boolean) Implements IPageableItemContainer.SetPageProperties
        Dim newPageIndex As Integer = (startRowIndex \ maximumRows)
        Me.PageSize = maximumRows
        If Me.PageIndex  newPageIndex Then
            Dim isCanceled As Boolean = False
            If databind Then
                ' create the event args and raise the event                 
                Dim args As New GridViewPageEventArgs(newPageIndex)
                Me.OnPageIndexChanging(args)
                isCanceled = args.Cancel
                newPageIndex = args.NewPageIndex
            End If
            ' if the event wasn't cancelled            
            ' go ahead and change the paging values            
            If Not isCanceled Then
                Me.PageIndex = newPageIndex
                If databind Then
                    Me.OnPageIndexChanged(EventArgs.Empty)
                End If
            End If
            If databind Then
                Me.RequiresDataBinding = True
            End If
        End If
    End Sub
    Private Shared ReadOnly EventTotalRowCountAvailable As New Object()
    Private ReadOnly Property IPageableItemContainer_StartRowIndex() As Integer Implements IPageableItemContainer.StartRowIndex
        Get
            Return Me.PageSize * Me.PageIndex
        End Get
    End Property
    Private Custom Event TotalRowCountAvailable As EventHandler(Of PageEventArgs) Implements IPageableItemContainer.TotalRowCountAvailable
        AddHandler(ByVal value As EventHandler(Of PageEventArgs))
            MyBase.Events.AddHandler(EventTotalRowCountAvailable, value)
        End AddHandler
        RemoveHandler(ByVal value As EventHandler(Of PageEventArgs))
            MyBase.Events.RemoveHandler(EventTotalRowCountAvailable, value)
        End RemoveHandler
        RaiseEvent(ByVal sender As System.Object, ByVal e As PageEventArgs)
        End RaiseEvent
    End Event
    Protected Overrides Function CreateChildControls(ByVal dataSource As IEnumerable, ByVal dataBinding As Boolean) As Integer
        Dim rows As Integer = MyBase.CreateChildControls(dataSource, dataBinding)
        ' if the paging feature is enabled, determine        
        ' the total number of rows in the datasource         
        If Me.AllowPaging Then
            ' if we are databinding, use the number of rows that were created,            
            ' otherwise cast the datasource to an Collection and use that as the count             
            Dim totalRowCount As Integer = If(dataBinding, rows, CType(dataSource, ICollection).Count)
            ' raise the row count available event             
            Dim pageableItemContainer As IPageableItemContainer = TryCast(Me, IPageableItemContainer)
            Me.OnTotalRowCountAvailable(New PageEventArgs(pageableItemContainer.StartRowIndex, pageableItemContainer.MaximumRows, totalRowCount))
            ' make sure the top and bottom pager rows are not visible            
            If Me.TopPagerRow IsNot Nothing Then
                Me.TopPagerRow.Visible = False
            End If
            If Me.BottomPagerRow IsNot Nothing Then
                Me.BottomPagerRow.Visible = False
            End If
        End If
        Return rows
    End Function
    Protected Overridable Sub OnTotalRowCountAvailable(ByVal e As PageEventArgs)
        Dim handler As EventHandler(Of PageEventArgs) = CType(MyBase.Events(EventTotalRowCountAvailable), EventHandler(Of PageEventArgs))
        If handler IsNot Nothing Then
            handler(Me, e)
        End If
    End Sub
End Class

Tags: asp.net, DataPager, SharePoint, SPGridView

Add a Comment

No Comments