.Net DataTables can be very useful when writing data-driven applications. However, they have one limitation: There is no obvious way to databind a grid (or other control) to an arbitrary list of datarows from a table.
You can bind to an entire table directly by setting a DataSource to the DataTable itself, and you can bind to a subset of a table by creating a DataView with a filter.
In general, you cannot bind to an
IEnumerable<T> (eg, a LINQ query); the databinding infrastructure can only handle an
IList (non-generic) or an
IListSource. This is true for any kind of datasource. Therefore, to bind to any LINQ query, you need to call
However, when binding to a DataTable, you can’t even use a
List<DataRow>. If you try, you’ll get four columns (RowError, RowState, Table, and HasErrors) and no useful information. This happens because the
List<DataRow> doesn’t tell the databinding infrastructure about the special properties of the DataRows. To understand the problem, some background is necessary
Databinding is controlled by the ListBindingHelper and TypeDescriptor classes. When you bind to a list, the ListBindingHelper.GetListItemProperties method is called to get the columns in the list. If the list implements the
ITypedList interface, its
GetItemProperties method is called. Otherwise, it will use TypeDescriptor to get the properties of the first item in the list. (this uses reflection)
The DataView class (which DataTable also binds through, using
ITypedList and returns DataColumnPropertyDescriptors that expose the columns in the table. This is why you can bind to a DataView or DataTable and see columns. However, when you bind to a
List<DataRow>, there is no
ITypedList that can return the columns as properties. It therefore falls back on reflection and shows the physical properties of the
To solve this issue, you need to wrap the list in a DataView so that you can take advantage of its
ITypedList implementation. You can do that using the
AsDataView() method. This method is only available on the
EnumerableRowCollection<T> classes; it cannot be called on an arbitrary LINQ query. You can only get an
EnumerableRowCollection<T> by calling special versions of the Cast, OrderBy, Where, and Select methods from a DataTable.
Therefore, you can databind to a simple LINQ query by calling
AsDataView() on the query. To bind to a
List<DataRow>, or to a more complicated query, you can use an ugly hack:
List<DataRow> list = ...; grid.DataSource = table.AsEnumerable() .Where(list.Contains) .AsDataView();
AsEnumerable() call is not needed for typed datasets.
You can also call
CopyToDataTable(), which will works on an arbitrary
IEnumerable<DataRow>. However, it makes deep copies of the rows, so it isn’t helpful if you want the user to update the data, or if you want the user to see changes made (in code) to the original datarows.