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:
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.
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:
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.
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.
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.
To do something other than the default processing, you can listen for three events: CellEditStarting
, CellEditValidating
and CellEditFinishing
.
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.
The CellEditValidating
event is triggered when the user tries to leave the cell editor. If the value that
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.