If you work with [DevEx] XtraGrid’s GridView then you know that it provides a relatively cumbersome way to read values of its grid cells: the values aren’t strong typed and the method is somehow not very OO oriented. It goes like this:
// read an int value from a cell int value = (int)gridView.GetRowCellValue(rowHandle, colAnIntColumn);
Note that you can’t read the value like this:
int value = (int)gridView.Rows[rowHandle].Value(colAnIntValue); // or int value = gridView.Rows[rowHandle].ColAnIntValue;
which would be more object oriented but probably the performance would suffer. If Value property would be strong typed it would be just great. But the most important feature would be to access the values through Rows collection. Why? Because one could use LINQ to query them. That is unfortunately impossible out of the box.
Here is a simplified sample code to find the rowHandle of the row with SOMEVALUE in one of its colAnIntColumn field:
int rowHandle; for (int f; f<gridView.RowCount; f++) { int value = (int)gridView.GetRowCellValue(rowHandle, intCol); if (value == SOMEVALUE) { rowHandle = f; break; } }
It isn’t really pretty, right. Hence the idea of using home made LINQ to XtraGrid.
First, we need a class that will represent a single GridView‘s row:
public class GridViewRow { internal readonly GridView GridView; internal readonly int RowHandle; public GridViewRow(GridView gridView, int rowHandle) { GridView = gridView; RowHandle = rowHandle; } // method that wraps GetRowCellValue method for getting a cell value // using column name public T Field<T>(string fieldName) { return (T)GridView.GetRowCellValue(RowHandle, fieldName); } // overloaded method that wraps GetRowCellValue method for getting a cell value // using column reference public T Field<T>(GridColumn column) { return (T)GridView.GetRowCellValue(RowHandle, column); } }
The code is pretty trivial. The class has to reference the source GridView and RowHandle that represents a row in GridView‘s notion. Note the two Field<T> overloaded methods that lets you get cell values in OO manner. Next, we need a collection of GridViewRow objects that implements IEnumerable<T> (IEnumerable<GridViewRow> in this case) interface (which in turn implements IEnumerable hence the two GetEnumerator() methods):
public class EnumerableGridViewRowCollection: IEnumerable<GridViewRow> { internal readonly GridView GridView; public EnumerableGridViewRowCollection(GridView gridView) { this.GridView = gridView; } public IEnumerator<GridViewRow> GetEnumerator() { for (int i = 0; i < GridView.RowCount; i++) yield return new GridViewRow(GridView, i); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } }
The core functionality here is public IEnumerator<GridViewRow> GetEnumerator() method. It uses yield return keyword to create GridViewRow instances for each row as query progresses through collection. From the perspective of garbage collection this shouldn’t be too huge problem because the objects are small and have a short lifetime usually. Thus they are relatively cheaply garbage collected. Furthermore one doesn’t work with million or rows in a grid – if you do, reconsider your approach; but rather in range of thousands. This class also holds reference to source GridView.
Now we have a GridViewRow class that represents a GridView‘s row and a collection of row objects that are dynamically generated for each query. The only step left is to build a way to get the EnumerableGridViewRowCollection out of GridView. We could use a helper class, pass GridView to EnumerableGridViewRowCollection directly, or derive a class out of GridView and add such a method. However the former aren’t really nice and the later is complex and doesn’t provide same functionality for other GridView derived views. Fortunately for us .net 3.5 introduced extension methods which are a perfect way to implement this functionality. Here is how I’ve built it:
public static class GridViewEnumerator { public static IEnumerable<GridViewRow> AsRowEnumerable(this GridView gridView) { return new EnumerableGridViewRowCollection(gridView); } }
This dude adds method AddRowEnumerable to GridView class and to all of derived classes.
LINQ to XtraGrid is now complete and here is rewritten sample query from above:
int rowHandle = gridView.AsRowEnumerable().First( row => row.Field<int>(intCol) == SOMEVALUE);
Doesn’t it look nicer and more compact than the query above? The beauty of the LINQ to XtraGrid is that it opens a whole LINQ world to XtraGrid’s GridView and descendants. Yes, you can use whatever LINQ construct that works on IEnumerable<T> such as:
var query = from row in this.AsRowEnumerable() where row.Field<int>(intCol) == SOMEVALUE group row by row.Field<string>(nameCol) into g orderby g.Key select g;
So, here it is. A simple LINQ to XtraGrid implementation. It doesn’t do everything, but hey, it is a start. Happy?
I realy wish i can afford a licence for that grid sometime!!! Lucky you!
Very nice idea, thanks!