The DataGrid control enables the user with powerful in-place editing capabilities accompanied with basic data validation. This exterior simplicity, however, involves non-trivial logic behind the scenes which we are going to explore.
We will start our exploration from the sequence in which data editing is carried out. The sequence starts from the moment the user clicks on a cell or presses the F2 key when a cell has previously been selected (for now we assume that the user works with an existing cell, as the sequence is slightly different when a new row is added and we will get back to it a little bit later). The grid brings the cell into the editable mode in response by calling the Edit method of the corresponding DataGridColumnStyle object. The column style, in turn, obtains the current cell value with its GetColumnValueAtRow method, displays an editing control (most commonly, a TextBox) within the cell and finally populates the control with the formatted representation of the current cell value.
Now the user can enter a new value into the cell. Once the cell value has changed, the DataGrid notifies the user that the current row has started editing by displaying a little pencil icon in the corresponding row header (see Figure 3).
Figure 3: Current Row is Being Edited
Behind the scenes, it is the responsibility of the corresponding DataGridColumnStyle to notify the grid that the cell has started editing by calling its own ColumnStartedEditing method.
TIP: It is important to notify the grid at the very moment the cell value has actually started to change, not at the moment the editing control is being displayed to the user.
Nothing worth our attention happens until the user has typed a new value and moved off the cell or has pressed Escape to restore the old value. Either way, the corresponding DataGridColumnStyle is put back to work with its Commit or Abort method invoked respectively. The abortion sequence is straightforward: the editing control is being hidden and the old cell value is restored from the data source. The updating sequence is much more complicated and consists of two basic phases:
- Data Validation
- Data Commit
The Data Validation phase may be different for various DataGridColumnStyles; we will take DataGridTextBoxColumn as an example. With this column style, the value entered is ensured that:
- It matches the pre-defined column format specified by the column’s Format and FormatInfo properties;
- It can be converted to the target data type determined by the data source. In plain English, this means that if the grid is bound to a DataTable and the underlying data column type is System.Int32, the column style checks whether the value entered can be successfully converted to a value of this type.
Note that the second check is the only check made against the data source. This means, in particular, that a Null value would easily pass the validation phase even if the corresponding column didn’t allow null values.
The column style actually pushes the new value back to the data source upon the Data Commit phase by calling its SetColumnValueAtRow method. If there were some problems, the update operation is rolled back and the user is notified about the error by displaying a message box. Believe me, this hard-coded behavior can sometimes be a real scourge as the developer might very likely wish to provide her own type of reaction on such an error. I am still racking my brain why DataGrid developers wouldn’t just throw an exception instead and let the developer decide how to handle it.
TIP: For DataTables and DataSets, the underlying row that has been modified changes its RowState property value to Modified. This can be used later to determine which rows has been edited by the user and should be somehow processed.