ObjectListView - Cookbook

Cell Editing Techniques

Editing the values of an ObjectListView

ListViews are normally used for displaying information. The standard ListView allows the value at column 0 (the primary cell) to be edited, but nothing beyond that. ObjectListView allows all cells to be edited. Depending on how the data for a cell is sourced, the edited values can be automagically written back into the model object.

The "editability" of an ObjectListView is controlled by the CellEditActivation property. This property can be set to one of the following values:

  • CellEditActivateMode.None - The ObjectListView cannot be edited (this is the default).
  • CellEditActivateMode.SingleClick - Subitem cells will be edited when the user single clicks on the cell. Single clicking on the primary cell does not start an edit operation - it selects the row, just like normal. Editing the primary cell only begins when the user presses F2.
  • CellEditActivateMode.DoubleClick - Double clicking on any cell, including the primary cell, will begin an edit operation. In addition, pressing F2 will begin an edit operation on the primary cell.
  • CellEditActivateMode.F2Only - Pressing F2 begins an edit operation on the primary cell. Clicking or double clicking on subitem cells does begin an edit operation.

Individual columns can be marked as editable via the IsEditable property (default value is True), though this only has meaning once the ObjectListView itself is editable. If you know that the user should not be allowed to change cells in a particular column, set IsEditable to False. Be aware, though, that this may create some surprises, resulting in user complaints like "How come I can't edit this value by clicking on it like I can on all the other cells?".

Once a cell editor is active, the normal editing conventions apply:

  • Enter or Return finishes the edit and commits the new value to the model object.
  • Escape cancels the edit.
  • Tab commits the current edit, and starts a new edit on the next editable cell. Shift-Tab commits the current end, and starts a new edit on the previous editable cell.

How are Cells Edited

The default processing creates a cell editor based on the type of the data in the cell. It can handle bool , int , string , DateTime , float and double data types. Once the cell editor has been created, it is given the cell's value via the control's Value property (if it has one and it is writable). If it doesn't have a writable Value property, it's Text property will be set with a text representation of the cell's value.

When the user has finished editing the value in the cell, the new value will be written back into the model object (if possible). To get the modified value, the default processing tries to use the Value property again. It that doesn't work, the Text property will be used instead.

This use of Value and Text properties applies to custom editor (created by event handlers) as well to the standard ones.

Updating the Model Object

Once the user has entered a new value into a cell and pressed Enter, the ObjectListView tries to store the modified value back into the model object. There are three ways this can happen:

1. CellEditFinishing Event Handler

You can create an event handler for the CellEditFinishing event (see below). In that handler, you would write the code to get the modified value from the control, put that new value into the model object, and set Cancel to true so that the ObjectListView knows that it doesn't have to do anything else. You will also need to call at least RefreshItem() or RefreshObject(), so that the changes to the model object are shown in the ObjectListView.

There are cases where this is necessary, but as a general solution, it doesn't fit my philosophy of slothfulness.

2. AspectPutter Delegate

You can install an AspectPutter delegate on the corresponding OLVColumn. If this delegate is installed, it will be invoked with the model object and the new value that the user entered. This is a neat solution.

3. Writable AspectName Property

If the column's AspectName is the name of a writable property, the ObjectListView will try to write the new value into that property. This requires no coding and certainly qualifies as the most slothful solution. But it only works if AspectName contains the name of a writable property. If the AspectName is dotted (e.g. Owner.Address.Postcode) only the last property needs to be writable.

If none of these three things happen, the user's edit will be discarded. The user will enter her or his new value into the cell editor, press Enter, and the old value will be still be displayed. If it seems as if the user cannot update a cell, check to make sure that one of the three things above is occurring.

How Can You Customise The Editing

To do something other than the default processing, you can listen for three events: CellEditStarting, CellEditValidating and CellEditFinishing.

CellEditStarting event

The CellEditStarting event is triggered after the user has requested to edit a cell but before the cell editor is placed on the screen. This event passes a CellEditEventArgs object to the event handlers. In the handler for this event, if you set e.Cancel to True , the cell editing operation will not begin. If you don't cancel the edit operation, you will almost certainly want to play with the Control property of CellEditEventArgs. You can use this to customise the default editor, or to replace it entirely.

For example, if your ObjectListView is showing a Color in a cell, there is no default editor to handle a Color. You could make your own ColorCellEditor , set it up correctly, and then set the Control property to be your color cell editor. The ObjectListView would then use that control rather than the default one. If you do this, you must fully configure your control, since the ObjectListView will not do any further configuration of the editor. So, to listen for the event, you would do something like this:

this.myObjectListView.CellEditStarting += new CellEditEventHandler(this.HandleCellEditStarting);

And your handler method might look something like this:

private void HandleCellEditStarting(object sender, CellEditEventArgs e) {
    if (e.Value is Color) {
        ColorCellEditor cce = new ColorCellEditor();
        cce.Bounds = e.CellBounds;
        cce.Value = e.Value;
        e.Control = cce;
    }
}

With this code in place, your spiffy ColorCellEditor will be shown whenever the user tries to edit a color in your ObjectListView.

CellEditValidating event

The CellEditValidating event is triggered when the user tries to leave the cell editor. If the value that

CellEditFinishing event

When the user wants to finish the edit operation, a CellEditFinishing event is triggered. If the user has cancelled the edit (e.g. by pressing Escape), the Cancel property will already be set to True. In that case, you should simply cleanup without updating any model objects. If the user hasn't cancelled the edit, you can by setting Cancel to True -- this will force the ObjectListView to ignore any value that the user has entered into the cell editor.

No prizes for guessing that you can refer to the Control property to extract the value that the user has entered and then use that value to do whatever you want. During this event, you should also undo any event listening that you have setup during the CellEditStarting event.

You can look in the demo at listViewComplex_CellEditStarting(), listViewComplex_CellEditValidating() and listViewComplex_CellEditFinishing() to see an example of handling these events.