Using SPGridView With DataPager (For Nicer Pagination)
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