DeleGrid - a paged GridView control

The DeleGrid is a control derived from the ASP.Net GridView, that delegates its data retrieval back out of the control. This allows the developer full control over the records that are retrieved, thus allowing proper paging to be implemented using whatever collection type you prefer.

Why the DeleGrid?

It came about because I wanted a nice way of implementing paging using NHibernate without having the grid know about it. I really didn’t want NHibernate to leave my data layer, so I needed a nice way of the grid calling my DAL with the paging parameters.

I didn’t want to utilise the ObjectDataSource because honestly, it made me feel dirty. I’m all for delegation and composition, but not when it means creating a control in my HTML that acts as a DAL. Additionally, I didn’t feel the ObjectDataSource was very type-safe, or refactor-friendly, with the method names and types exposed in the HTML. Granted, the IDE would probably pick it up, but I don’t want to risk a runtime failure on it.

Using the DeleGrid (aka The Example)

After getting the source or assembly and doing the usual song-and-dance, add a reference to the control to your page:

<%@ Register Assembly="JAGregory.Controls.DeleGrid" Namespace="JAGregory.Controls" TagPrefix="jag" %>

Then create an instance of the control, turning the paging on and setting the correct page size:

<jag:DeleGrid ID="grid" runat="server" AllowPaging="true" PageSize="4" />

Now you have a control set up, however it sill won’t bind correctly. So, you need to attach the event handlers in the code-behind.

protected override void OnInit(EventArgs eventArgs)
{
  base.OnInit(eventArgs);

  grid.TotalRecordCountRequest += delegate {
    // code to get total
  };
}

Starting with the TotalRecordCountRequest, this event is raised when the grid needs to know how many records in total your grid is going to be displaying. This number is the cumulative count of all the pages. I’m going to use a simple repository pattern to factor away my DAL logic.

The OnInit method is now:

protected override void OnInit(EventArgs eventArgs)
{
  base.OnInit(eventArgs);

  ProductRepository repos = new ProductRepository();

  grid.TotalRecordCountRequest += delegate {
    return repos.GetTotal();
  };
}

Now your grid knows how many records it has overall, however we still haven’t told it how to actually get the data. So now to put the code in the PageDataRequest handler. This event is raised when the grid is needing a new page of data, this will get called once on initial data-bind, then again every time you change the page (or sorting etc…).

The OnInit method is now:

protected override void OnInit(EventArgs eventArgs)
{
  base.OnInit(eventArgs);

  ProductRepository repos = new ProductRepository();

  grid.TotalRecordCountRequest += delegate {
    return repos.GetTotal();
  };
  grid.PageDataRequest += delegate(object sender, DataRequestEventArgs e) {
    return repos.GetRange(e.Start, e.Size);
  };
}

The event-handler receives an instance of DataRequestEventArgs, which contains the start index of the current page of data, and the number of records in a page. It also contains a SortField and SortDirection, for when sorting is enabled on the grid; however, we aren’t utilising them in this example.

Finally we just need to bind the grid on page load. We don’t re-bind the grid on post-back, due to that being handled internally in the DeleGrid.

protected override void OnLoad(EventArgs eventArgs)
{
  base.OnLoad(eventArgs);

  if (!IsPostBack)
    grid.DataBind();
}

That’s all there is to it!

You don’t need to use delegates, the normal event-handler syntax is fine (and probably preferred for larger examples). I just did it this way for brevity’s sake.

Further reading…

Testing

I’ve written a small number of tests that cover the implementation of the grid as best I can. There was only so-far I was willing to go to test the control, as it’s heavily tied to the ASP.Net implementation; which can get pretty messy for testing without using something like NUnitASP, which was a bit much for one control.. I’ve got coverage of about 85% of the code, which I’d say is pretty reasonable anyway.

Sorting

As mentioned above, you can implement sorting in your handlers by accessing the SortField and SortDirection properties of the event arguments.

DeleGrid.AlwaysRequestTotal

By default the DeleGrid only requests the total number of records on the initial data-bind, however if you see this as being a problem (such as with rapidly changing data-sets), you may want to set this property to true so it refreshes the total on every data-bind.

Downloads

Note from future James: This is long gone.