External IEnumerables

It is easy enough to make use of the Items collection supplied along with the DataGrid but most of the time the data that is displayed comes from an external source - typically a database connection. It is easy to get a DataGrid to use an external collection object. All you have to do is set its ItemsSource property. To see this in action we first create a suitable object type to hold the data:

At the end of this code block MyList contains three Person objects suitably initialised. All we have to do to make the DataGrid display this collection is:

dataGrid1.ItemsSource=myList;

After this assignment the DataGrid's Items property references and hence uses myList.

We can also get the grid to generate the necessary columns with the correct bindings for us by setting its AutoGenerateColumns property to true. In fact as this is its default we generally get the columns autogenerated without having to do anything. The result is a three-column grid with two DataGridTextColumns and one DataGridCheckBoxColumn. You could have added and bound the three columns in code using the same sort of code used earlier.

There is a subtlety hidden here. The ItemSource property references the original list, i.e. MyList, but the Items property references an ItemCollection which the List has been converted to. It is always the derived ItemCollection which is used to display the data. This means that there is always a current item and you can use the ItemCollection methods to move the current position. Notice also that the original variable that references the collection you assign to the DataGrid can go out of scope without any problem as the DataSource property still provides a reference to the object.

Data editing

The easiest way of editing the data in the DataGrid is to simply set the properties that allow the user to do the job interactively. You can set or unset CanUserAddRows, CanUserDeleteRows, CanUserReorderColumns and so on. Just look up properties that start with "Can". Notice that if the user adds a row or modifies the data then the modifications are made to the collection assigned to ItemsSource. This isn't an example of two way databinding, in fact databinding isn't being used - it is simply that the collection providing the data that the DataGrid shows is used to store the new or modified data.

Now we come to the subject of editing data from code and this is where the questions about rows, columns and even cells tend to crop up. If you have followed the discussion of how the DataGrid works then you will be quite clear that there is no need to think in terms of rows or columns or cells for that matter. The raw material of the DataGrid is a collection of objects which roughly correspond to the rows in the alternative model and each object has a collection of properties which correspond to the columns or the cells depending on how you want to look at it.

However there are equivalents of the row and column oriented access methods and they are sometimes useful.

To access a "cell" value

To access the data stored in the grid at a given row and column you would retrieve the item that represents the row, cast it to the correct type and use the property that corresponds to the column. For example, to retrieve the name of the first item in the DataGrid in the previous example:

string name = ((Person) dataGrid1.Items[0]).name;

To modify a "cell" value

To modify a cell value at a given row and column you simply use the recipe given above but assign to the property. For example to change the value of the name in the first item in the DataGrid in the previous example you would use:

((Person)dataGrid1.Items[0]).name = "Tom";

If you consider the situation for a moment then it is also clear that, as long as you still have a reference to it, you can modify the original collection just as easily as the grid. For example you could change MyList in the same way.

To access the currently selected row

This is just a matter of using the SelectedItem property:

string name = ((Person) dataGrid1.SelectedItem).name;

To access the currently select cell

This is perhaps the most tricky thing to do and it takes us into the use of the Cell and CellInfo objects which is perhaps the closest we get to the traditional rows, columns and cells approach.

First we have to switch the DataGrid into cell selection mode using its SelectionUnit property:

dataGrid1.SelectionUnit = DataGridSelectionUnit.Cell;

Now the user can select a block of cells. To access the selected cells we have to use the SelectedCells collection. This returns a collection of DataGridCellInfo objects. To select the first we would use:

DataGridCellInfo cell = dataGrid1.SelectedCells[0];

Now we have a DataGridCellInfo object we can actually access the cell itself. This isn't the cell value however but whatever control is used to display the cell contents. In the case of a text column this is a TextBlock control. To retrieve this we have to use a complicated multi-step approach. First get the column that the cell is in:

cell.Column

Now we can get the CellContent at the intersection of the column and the item that the selected cell is in:

cell.Column.GetCellContent(cell.Item)

This returns a TextBlock which we can finally access the text stored in the cell, but only with the help of a suitable cast. Putting all this together gives:

This is convoluted and I have to admit that there might be a better way but this is typical of the sort of thing that happens once you have to go back to thinking in terms or rows, columns and cells. Much better to think of collections of objects, columns and properties.