When was the last time you used the ASP.NET DataGrid control with something other than an ADO.NET provider? Chances are, like many developers, you haven't. The DataGrid control has such a well-publicized relationship with ADO.NET that it's difficult to imagine it doing anything useful on its own. Yet, by understanding and programming to the interfaces the DataGrid uses to obtain data, you can turn it into an extremely flexible tool that can display any type of information on a web page.
By now, every ASP.NET programmer is familiar with the following two lines of code:
myGrid.DataSource = mySqlDataReader;
myGrid.DataBind();
The first line tells the grid control to accept the DataReader
mySqlDataReader as the data source, and the second line instructs it to go ahead and display whatever is in the reader in an HTML table. The key to getting the data grid to accept a custom provider is to know which interfaces of ADO.NET it uses to obtain the data, and then developing your code to implement those interfaces. Although there are a variety of such constructs you can choose from, the most suitable one for a read-only presentation is the IEnumerable interface, which you'll find in the System.Collections namespace.
To implement IEnumerable, you need to override only one member function,
GetEnumerator(), whose sole purpose is to return an IEnumerator object, which will allow the grid control to "walk through" your data. The slightly more complex IEnumerator interface consists of three member functions, each of which implicitly assumes that you maintain an index pointer to keep track of the current item being enumerated. The three members are:
Reset(): This method moves the index pointer to point to the initial position in the enumerated collection, regardless of its current position. You should invoke this method yourself before handing your provider to the grid, so that the control knows exactly where to start traversing your data.
MoveNext(): This method moves the pointer to the next position. If the new position is final, or if you are already on the final position, the method should return
false, signaling to the caller that there is no more data. In all other cases, return
true.
Current(): This property should return the object pointed to by the index pointer. The ASP.NET DataGrid invokes this property after every successful call to
MoveNext(). To populate the grid correctly, your code should create an object that represents the current row and return it to the grid for rendering.
If the DataGrid calls
Current() at the initial or the final positions, your implementation should raise an InvalidOperationException. In practice, this exception suggests a bug in your
MoveNext() method, which is probably not returning
false when the pointer moves past the end of the list, leading the grid to think there is more data to be retrieved, whereas in fact it has already retrieved all the rows.
There are no special requirements on the object returned by
Current(), except that the names of its public properties should correspond to column names, and the property values should correspond to the actual value of the column in question.
Developers familiar with ADO will immediately recognize this structure as very similar to the forward-only/read-only recordset model, as shown in the state transition diagram of
Figure 1. To get the information from your data source, the grid uses an algorithm similar to the one represented by the flowchart in
Figure 2.
Note that, in the flowchart, the grid never calls the
Reset() method, meaning that it is
your responsibility to call this method before you hand over your provider to it. Failure to do so will result in an uninitialized data source, and you will most likely witness a mysterious loss of rows during runtime.