(download at the end of this post)
Believe it or not, there are still poor souls out there stuck with antiquated equipment. In the previous post on highlighting DataGrid rows, I recounted a situation in which I found myself needing to implement myself a feature in .NET 1.1 that is easy in .NET 2.0 or above.
To summarize, instead of handling an event, I had to go down a much pricklier path:
- Via polymorphism and event-handling dexterity, enable arbitrary highlighting of rows in a DataGrid.
- Force the colored rows to behave when sorting on any column.
- Mourn the fact that for relatively large DataSets, the re-coloring of rows after a sort is a performance problem, the solution of which is to implement paging.
To the best of my knowledge, paging a System.Windows.Forms DataGrid is not supported in any version of .NET.
When I started thinking about this post on Monday, I was just going to outline possible solutions:
- Out-of-memory paging: hit your data source as the user pages through data, only loading pages that need to be loaded for viewing
- Out-of-memory paging with cache: same as above except load the current page, previous page, and next page
- In-memory paging: load entire DataSet and page in memory
Upon first encountering the problem while on The Project, my first instinct was either of the first two options. While considering the solution on Monday, this did not change.
But I have since become much more wise and lazy since The Project ended, and honestly, I don’t like writing so much code, because bugs will surely follow. The first two solutions are not ideal because they involve knowing what your data source is and most likely changing to accommodate extra parameters to indicate how many records to fetch for a given query.
In The Project’s case, SQL Server inexplicably does not allow you to use a stored procedure in a FROM clause. So to do any out-of-memory paging would require changes to the s-procs.
Then, to maintain proper layering, report services and repositories would have to change for the same reason. Add caching to the mix and this is a minor headache.
Finally, out-of-memory paging would be good for performance problems related to the size of the DataSet. No such problem existed, so tackling it at this point would be over-engineering.
So, I settled on in-memory paging. At first I was only going to outline the solution with UML and a half-hearted code example. As usual, I wrote the example first. Then I looked deep into its eyes. And just like that episode of Seinfeld where Jerry shaves his chest:
Jerry: I did something stupid.
Kramer: What did you do?
Jerry: Well I was shaving. And I noticed an asymmetry in my chest hair and I was trying to even it out. Next thing I knew, (high pitched voice) Gone.
one thing led to another, half a day of coding and debugging led to two days, and at the end of this post you can download a WinForms DataGrid Pagination demo.
Since you can download the VS project in all its glory, I will only list the PaginatedDataGrid interface:
public interface PaginatedDataGrid
This is supposed to be self-explanatory. The only thing that would need to be explained is that the PaginatedDataGrid is a wrapper around an existing DataGrid, and that DataGrid must use a DataSet as its DataSource. Hence, the download contains only one implementation of this interface. If I ever wanted this code up on CodeProject, I’d implement the remaining five.
PaginatedDataGrid is a bit like the Decorator pattern in that it can be turned on and off at run-time, save the exact inheritance structure of the actual pattern.
Also, you’ll notice this example in C# instead of the usual VB. I have started re-implementing the core of The Project in my spare time so I can fix all of the ridiculous design mistakes I made, practice TDD, and learn C#.
I wrote this example in .NET 3.5. It is ready to run, except you need to provide your own DataSet, however you wish to do so. I did it through SQL Server and I left the boilerplate code for that route in the demo if you choose to do so.
Checking the “Pagination on” checkbox will turn the feature on and off so you can see the performance problem I was facing. You can also choose your own page size by entering a valid positive integer (I didn’t sanity-check this field so be nice) and tabbing off the textbox.
Here is a sampling of the interesting bugs I had to destroy:
- When moving from page to page, sometimes page P’s first row, if to be highlighted, wouldn’t be highlighted until you either moved to page P+1 or highlighted the rows twice.
- Getting a truly “deep copy” of the rows of the initial DataSet proved to be more of a challenge than it should’ve been.
- Adding those rows back to the DataTable of the DataSet had to be accomplished with a new object array because you can’t create a new DataRow and can’t add the existing one because of an ArgumentException that I didn’t feel like dealing with.
- Sorting on a page would only sort that page, when it should sort the entire DataSet and then reload the page you’re on for that DataSet.
ZIP file can be downloaded here. Any feedback is much appreciated.