Menu

Data Grid Editing

SourceForge Editorial Staff

Spark DataGrid Editing - Functional and Design Specification


Introduction

The Spark DataGrid supports interactive editing of DataGrid cell values using an "item editor" or the cell's item renderer itself.

An item editor is a component that's temporarily displayed on top of the cell's item renderer. The item editor provides a UI for editing and saving the value that the cell's item renderer displays. An item editor can be any Spark component class that implements IGridItemEditor and each DataGrid column can specify its own item editor class. Just like an item renderer, the item editor displays a value based on the dataProvider item for the row it's displayed on. By default, the item editor is displayed when the user clicks on an already-selected cell, and then only if the editable property for the DataGrid and the corresponding GridColumn are true. When the item editor appears it gets the keyboard focus, typing enter causes the editor's value to be saved and the editor dismissed, and typing escape will cause the editor to be dismissed without saving.

In some cases the item renderer itself will serve as the cell's editor. If a column's rendererIsEditable propety is set to true, then clicking on a selected cell within the column (or reselecting the cell with the keyboard) will cause the the keyboard focus to be moved to the item renderer. In this case, no other editing support is provided.

GridItemEditor is a base class used to create item editors. The DefaultGridItemEditor and the ComboBoxGridItemEditor are based on the GridItemEditor class. The GridItemEditor contains a default implementation of IGridItemEditor so that typical item editors will only override a few properties and methods.

Item Editor Lifecycle

An item editor "session" starts when an item editor is created in response to a user gesture, like clicking on a selected cell, or an explicit call to the DataGrid startItemEditorSession() method. Item editors target a single cell, which is specified by a row and column index.

Before an item editor is displayed, a "gridItemEditorSessionStarting" GridItemEditorEvent is dispatched to the DataGrid. Handlers can short-circuit the editing session by canceling the event with the Event preventDefault() method, or change the column's itemEditor class.

Item editors are created using the IGridItemEditor factory specified by the column's itemEditor property. Like item renderers, item editor instances are reused. If a column doesn't specify an itemEditor then the DataGrid's itemEditor is used. The default item editor, DefaultGridItemEditor, uses a TextArea component to display its value.

Before an item editor is displayed its rowIndex, column, and data properties are set. The data property is the value of dataProvider.getItemAt(rowIndex).

IGridItemEditor classes should use the set method for the data property to initialize the the value of their editing component. The default implementation of GridItemEditor uses datacolumn.dataField to set the value property of GridItemEditor. This allows subclasses to override the value setter to push the value into one or more editor controls.

Here's an example of a custom item editor, similar to the default item editor, that displays its value with a TextArea:

<s:itemEditor>
    <fx:Component>
    <s:GridItemEditor>
        <s:TextArea id="valueDisplay"/>
        <fx:Script>
        <![CDATA[
            override public function get value():Object
            {
            return valueDisplay.text;            
            }

            override public function set value(newValue:Object):void
            {
            valueDisplay.text = newValue.toString();
            }                                        
        ]]>
        </fx:Script>
    </s:GridItemEditor>
    </fx:Component>
</s:itemEditor>

Just before the item editor is made visible, i.e. just before its visible property is set to true, its prepare() method is called. When prepare() is called, the editor's size and location will have been set and the editor's layout will have been validated. Developers can override the prepare() method to fine tune any aspect of the editor. In the following example, the prepare() method is used to vertically inset the default item editor's textArea:

<s:itemEditor>
    <fx:Component>
    <s:DefaultGridItemEditor>
        <fx:Script>
        <![CDATA[
            override public function prepare():void
            {
            super.prepare();
            textArea.y += 8;
            textArea.height -= 16; 
            }
        ]]>
        </fx:Script>
    </s:DefaultGridItemEditor>
    </fx:Component>
</s:itemEditor>

After the editor has been made visible, a "gridItemEditorStart" GridItemEditorEvent event is dispatched to the DataGrid.

When at item editor is first displayed it's given the keyboard focus and its setFocus() method is called. The setFocus() method can be used to delegate the focus to some element of the item editor. For example the DefaultGridItemRenderer's setFocus() method just sets the focus on its TextArea.

Once it's been displayed the item editor enables the user to select a new value and the "edit session" is underway. The edit session is concluded when the DataGrid endEditorSession() method is called. By default this happens when the user presses the enter or escape keys, which are interpreted as "save" or "cancel" respectively. The session is saved if the users clicks outside of the editor or any time the editor loses keyboard focus. The session is also saved when any of the defined keyboard accelerators are used to save the edit session and open an editor in a different cell. If the session was saved() then the editor's save() method is called.

The default GridItemRenderer implementation of the save() method can convert string values to numbers or booleans if necessary. Editors whose values aren't strings, numbers, or booleans should override the save() method, for example:

<s:itemEditor>
    <fx:Component>
    <s:DefaultGridItemEditor>
        <fx:Script>
        <![CDATA[
            override public function save():void
            {
                data.dataField = myValueConverter(value);
            }
        ]]>
        </fx:Script>
    </s:DefaultGridItemEditor>
    </fx:Component>
</s:itemEditor>

After the editor's save() or cancel() method returns, an informational gridItemEditorSessionSave or a gridItemEditorSessionCancel GridItemEditor event is dispatched.

In addition to DefaultGridItemEditor, which displays value.toString() and can save string or number values, there's ComboBoxGridItemEditor for small lists of values. This editor displays its dataProvider, a list of values, with a ComboBox. It inherits the same support as DefaultGridItemEditor for handling string, number, and boolean values. For example to enable the user to set the value of a cell to an integer, one of 0,1,2,3:

<s:itemEditor>
    <fx:Component>
    <s:ComboBoxGridItemEditor>
        <s:dataProvider>
        <s:ArrayList>
            <fx:int>0</fx:int>
            <fx:int>1</fx:int>
            <fx:int>2</fx:int>
            <fx:int>3</fx:int>
        </s:ArrayList>
        </s:dataProvider>
    </s:ComboBoxGridItemEditor>
    </fx:Component>
</s:itemEditor>

Keyboard Accessibility

The DataGrid editing support defines the following keyboard bindings. Note that the tab bindings wrap if necessary. For example tabbing from the last column, moves to the first column on the next row. In all cases, the destination cell is always scrolled completely into view.

F2 - edits the cell indicated by the caret if cell selection mode is enabled. If row selection mode is enabled, and a cell hasn't been edited before, then the first visible cell in the row indicated by the caret is edited. Otherwise the cell in the last edited column, in the caret row, is edited.

Enter - saves the editor's value and closes the editor.

Esc - closes the editor without saving.

Tab - save and move to the next column

Shift+Tab - save and move to the previous column.

Ctrl+. - cancels the edit (Halo editors supported this).

Ctrl+Enter - save and move to the next row, same column.

Ctrl+Shift+Enter - save and move to the previous row, same column.

Mouse Gestures

Single click -An editor is opened when a single click event is received on a selected cell. If the shift or ctrl keys are down on the click, then the editor will not be opened because the shift and ctrl keys are assumed to be used to modify the grid selection.

Double click -An editor is not opened if a double click event is received on a cell. By default, double click events are not enabled. To enable double click events, set the doubleClickEnabled property to true on the DataGrid .

DataGrid API Description

The API for the DataGrid class shown below is incomplete, only the properties relevant to this section are shown.

package spark.components
{
public class DataGrid extends SkinnableContainerBase 
    implements IFocusManagerComponent, IGridItemRendererOwner, IIMESupport
{  
/**
 *  Name of the class of the itemEditor to be used if one is not
 *  specified for a column.  This is a way to set
 *  an item editor for a group of DataGrids instead of having to
 *  set each one individually.  If you set the DataGridColumn's itemEditor
 *  property, it supercedes this value.
 *  @default null
 *  
 *  @langversion 3.0
 *  @playerversion Flash 10
 *  @playerversion AIR 1.5
 *  @productversion Flex 4
 */
<a href="Style%28name%3D%26quot%3BdefaultDataGridItemEditor%26quot%3B%2C%20type%3D%26quot%3BClass%26quot%3B%2C%20inherit%3D%26quot%3Bno%26quot%3B%29">Style(name="defaultDataGridItemEditor", type="Class", inherit="no")</a>

    //----------------------------------
    //  editorIndicator
    //----------------------------------

    <a href="Bindable">Bindable</a>
    <a href="SkinPart%28required%3D%26quot%3Bfalse%26quot%3B%2C%20type%3D%26quot%3Bmx.core.IFactory%26quot%3B%29">SkinPart(required="false", type="mx.core.IFactory")</a>

    /**
     *  The IVisualElement class used to render a background behind
     *  item renderers that are being edited. Item renderers may only be edited
     *  when the data grid and the column are both editable and the
     *  column sets rendererIsEditable to true.
     */
    public var editorIndicator:IFactory;

    /**
     *  A flag that indicates whether the IME should
     *  be enabled when the component receives focus.
     *
     *  If the editor is up, it will set enableIME
     *  accordingly.
     *
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 4.5
     */
    public function get enableIME():Boolean

    /**
     *  The default value for the GridColumn imeMode property, which specifies
     *  specifies the IME (input method editor) mode.
     *  The IME enables users to enter text in Chinese, Japanese, and Korean.
     *  Flex sets the specified IME mode when the control gets the focus,
     *  and sets it back to the previous value when the control loses the focus.
     *
     * The flash.system.IMEConversionMode class defines constants for the
     *  valid values for this property.
     *  You can also specify null to specify no IME.
     *
     *  @default null
     */
    public function get imeMode():String
    public function set imeMode(value:String):void

    /**
     *  The default value for the GridColumn itemEditor property, which specifies
     *  the IGridItemEditor class used to create item editor instances.
     * 
     *  @default null.
     *
     */
    public function get itemEditor():IFactory
    public function set itemEditor(value:IFactory):void

   /**
     *  A reference to the currently active instance of the item editor, 
     *  if it exists.
     *
     *  To access the item editor instance and the new item value when an 
     *  item is being edited, you use the itemEditorInstance 
     *  property. The itemEditorInstance property
     *  is not valid until the itemEditorSessionStart event is 
     *  dispatched.
     *
     *  The DataGridColumn.itemEditor property defines the
     *  class of the item editor and, therefore, the data type of the item
     *  editor instance.
     */
    public function get itemEditorInstance():IGridItemEditor

    /**
     *  Starts an editor session on a selected cell in the data grid.
     * 
     *  A startItemEditorSession event is dispatched before
     *  an item editor is created. This allows a listener dynamically change 
     *  the item editor for a specified cell. 
     * 
     *  The event can also be cancelled with preventDefault(), to prevent the 
     *  editor session from being created.
     * 
     *  @param rowIndex The zero-based row index of the cell to edit.
     *  @param columnIndex The zero-based column index of the cell to edit.  
     * 
     *  @return true if the editor session was started. Returns false if
     *  the editor session was cancelled.
     */ 
    public function startItemEditorSession(rowIndex:int, columnIndex:int):Boolean

    /**
     *  Closes the currently active editor and optionally saves the editor's value
     *  by calling the item editor's save() method.  If the cancel parameter is true,
     *  then the editor's cancel() method is called instead.
     * 
     *  @param cancel If false the data in the editor is saved. 
     *  Otherwise the data in the editor is discarded.
     *
     *  @return true if the editor session was saved, false if the save was
     *  cancelled.  
     */ 
    public function endItemEditorSession(cancel:Boolean = false):Boolean

    //... Remaining DataGrid API appears elsewhere in this spec
}
}

GridColumn

The API for the GridColumn class shown below is incomplete, only the properties relevant to this section are shown.

package spark.components.gridClasses
{

public class GridColumn extends EventDispatcher
{
    /**
     *  @private
     */
    private var _imeMode:String = null;

    <a href="Inspectable%28environment%3D%26quot%3Bnone%26quot%3B%29">Inspectable(environment="none")</a>

    /**
     *  Specifies the IME (input method editor) mode.
     *  The IME enables users to enter text in Chinese, Japanese, and Korean.
     *  Flex sets the specified IME mode when the control gets the focus,
     *  and sets it back to the previous value when the control loses the focus.
     *
     * The flash.system.IMEConversionMode class defines constants for the
     *  valid values for this property.
     *  You can also specify null to specify no IME.
     *
     *  @default null
     */
    public function get imeMode():String
    public function set imeMode(value:String):void

    /**
     *  A factory for IGridItemEditors used to edit individual grid cells in 
     *  this column.
     *
     *  If this property is null, and the column grid's owner is a DataGrid, 
     *  then the value of the DataGrid's itemEditor property is used.   If no
     *  item editor is specified then DefaultGridItemEditor is used.
     * 
     *  @default null
     */
    public function get itemEditor():IFactory
    public function set itemEditor(value:IFactory):void

    /**
     *  Determines whether any of the item renderer's controls are editable.
     *  If the column is editable, the focusable controls in the item renderer
     *  will be given keyboard focus when the user starts editing the item
     *  renderer.
     * 
     *  @default false
     */
    public function get rendererIsEditable():Boolean


    //... Remaining GridColumn API appears elsewhere in this spec
}
}

IGridItemEditor API Description

package spark.components
{
/**
 *  Grid item editors must implement this interface. 
 * 
 *  All of the item editor's properties are set by the DataGrid during 
 *  the start of the editor session. The data property is the 
 *  last property set. When the data property is set a grid item editor should
 *  set the value of the editor's controls. Next the editor's 
 *  prepare() method is called. IGridItemEditor 
 *  implementations should override the preprare() method to make any final
 *  adjustments to its properties or any aspect of its visual elements. 
 *  When the editor is closing the discard() method is called.
 *  
 * When the editor is closed the input value can be saved or cancelled. If saving, 
 * the save() function is called by the editor.
 * 
 */
public interface IGridItemEditor extends IDataRenderer, IVisualElement, 
                                 IFocusManagerComponent, IIMESupport
{
    /**
     *  The data grid that owns this editor.
     */
    function get dataGrid():DataGrid;

    /**
     *  The column that is being edited.
     */
    function get column():GridColumn;
    function set column(value:GridColumn):void;

    /** 
     *  The zero-based index of the column that is being edited.
     */ 
    function get columnIndex():int;

    /** 
     *  The zero-based index of the row that is being edited.
     */ 
    function get rowIndex():int;
    function set rowIndex(value:int):void;

    /**
     *  Called after the editor has been created and sized but before the 
     *  editor is visible. This is an oppurtunity to adjust the look of
     *  the editor before it becomes visible.
     *  
     *  This function should only be called by the data grid.
     */ 
    function prepare():void;

    /**
     *  Called just before the editor is closed. This is a chance
     *  of clean up anything that was set in prepare().
     *  
     *  This function should only be called by the data grid.
     */ 
    function discard():void;

    /**
     *  Saves the value in the editor back into the item renderer's
     *  data. This function calls validate() to verify
     *  the data may be saved. If the data is not valid, then the
     *  data is not saved and the editor is not closed.
     * 
     *  This function should only be called by the data grid. To save
     *  and close the editor call the endItemEditorSession()
     *  function of the data grid owner.
     */  
    function save():Boolean;
}
}

GridItemEditorEvents Dispatched to DataGrid

The events in this section are defined by the DataGrid class because they are dispatched by the active item editor to the DataGrid, as the item editor moves through its editing session lifecycle. Most of the events are "informational", which is to say that they're provided for listeners that want to stay in sync with the item editor. The startGridItemEditorSession event is the exception: listeners can veto the editing session by cancelling this event.

/**
 *  Dispatched when a new item editor session has been requested. A listener can
 *  dynamically determine if a cell is editable and cancel the edit (with 
 *  preventDefault()) if it is not. A listener may also dynamically 
 *  change the editor that will be used by assigning a different item editor to
 *  a column.
 * 
 *  If this event is cancelled the item editor will not be created.
 *
 *  @eventType spark.events.GridItemEditorEvent.GRID_ITEM_EDITOR_SESSION_STARTING
 */
<a href="Event%28name%3D%26quot%3BgridItemEditorSessionStarting%26quot%3B%2C%20type%3D%26quot%3Bspark.events.GridItemEditorEvent%26quot%3B%29">Event(name="gridItemEditorSessionStarting", type="spark.events.GridItemEditorEvent")</a>

/**
 *  Dispatched immediately after an item editor has been opened. 
 *
 *  @eventType spark.events.GridItemEditorEvent.GRID_ITEM_EDITOR_SESSION_START
 */
<a href="Event%28name%3D%26quot%3BgridItemEditorSessionStart%26quot%3B%2C%20type%3D%26quot%3Bspark.events.GridItemEditorEvent%26quot%3B%29">Event(name="gridItemEditorSessionStart", type="spark.events.GridItemEditorEvent")</a>

/**
 *  Dispatched after the data in item editor has been saved into the data provider
 *  and the editor has been closed.  
 *
 *  @eventType spark.events.GridItemEditorEvent.GRID_ITEM_EDITOR_SESSION_SAVE
 */
<a href="Event%28name%3D%26quot%3BgridItemEditorSessionSave%26quot%3B%2C%20type%3D%26quot%3Bspark.events.GridItemEditorEvent%26quot%3B%29">Event(name="gridItemEditorSessionSave", type="spark.events.GridItemEditorEvent")</a>

/**
 *  Dispatched after the item editor has been closed without saving its data.  
 *
 *  @eventType spark.events.GridItemEditorEvent.GRID_ITEM_EDITOR_SESSION_CANCEL
 *  
 *  @see spark.components.DataGrid.itemEditorInstance
 */
<a href="Event%28name%3D%26quot%3BgridItemEditorSessionCancel%26quot%3B%2C%20type%3D%26quot%3Bspark.events.GridItemEditorEvent%26quot%3B%29">Event(name="gridItemEditorSessionCancel", type="spark.events.GridItemEditorEvent")</a>

GridItemEditorEvent API Description

package spark.events
{

/**
 *  This class represents events that are dispatched over 
 *  the life cycle of an item editor.
 *
 *  The life cycle starts with the dispatch of an 
 *  GRID_ITEM_EDITOR_SESSION_STARTING event. This event may be cancelled by a
 *  listener to stop the creation of an editing session.
 * 
 *  Next, after the editor is opened the GRID_ITEM_EDITOR_SESSION_START
 *  is dispatched to notify listeners that the editor has been opened.
 * 
 *  The editing session can be saved or cancelled. If the session is saved
 *  then the GRID_ITEM_EDITOR_SESSION_SAVE event is dispatched.
 *  If the editor is cancelled a GRID_ITEM_EDITOR_SESSION_CANCEL
 *  event is dispatched.
 * 
 */
public class GridItemEditorEvent extends Event
{
    include "../core/Version.as";

    /**
     *  The GridItemEditorEvent.GRID_ITEM_EDITOR_SESSION_STARTING 
     *  constant defines the value of the type property of the
     *  event object for a startGridItemEditorSession event. 
     *  Dispatched when a new item editor session has been requested. A listener
     *  can dynamically determine if a cell is editable and cancel the edit (with 
     *  preventDefault()) if it is not. A listener may also dynamically 
     *  change the editor that will be used by assigning a different item editor
     *  to a column.
     *   
     *  @eventType gridItemEditorSessionStarting
     */
    public static const GRID_ITEM_EDITOR_SESSION_STARTING:String = "gridItemEditorSessionStarting";

    /**
     *  The GridItemEditorEvent.GRID_ITEM_EDITOR_SESSION_START 
     *  constant defines the value of the type property of the
     *  event object for a openGridItemEditor event. 
     *  Dispatched immediately after an item editor has been opened. 
     *   
     *  @eventType gridItemEditorSessionStart
     */
    public static const GRID_ITEM_EDITOR_SESSION_START:String = "gridItemEditorSessionStart";

    /**
     *  The GridItemEditorEvent.GRID_ITEM_EDITOR_SESSION_SAVE 
     *  constant defines the value of the type property of the
     *  event object for a saveGridItemEditor event. 
     *  Dispatched after the data in item editor has been saved into the data 
     *  provider and the editor has been closed.  
     *   
     *  @eventType gridItemEditorSessionSave
     */
    public static const GRID_ITEM_EDITOR_SESSION_SAVE:String = "gridItemEditorSessionSave";

    /**
     *  The GridItemEditorEvent.GRID_ITEM_EDITOR_SESSION_CANCEL 
     *  constant defines the value of the type property of the
     *  event object for a cancelridItemEditor event. 
     *  Dispatched after the item editor has been closed without saving its data.  
     *   
     *  @eventType gridItemEditorSessionCancel
     * 
     *  @see spark.components.gridClasses.GridColumn;
     */
    public static const GRID_ITEM_EDITOR_SESSION_CANCEL:String = "gridItemEditorSessionCancel";

    /**
     *  Constructor.
     *
     *  @param type The event type; indicates the action that caused the event.
     *
     *  @param bubbles Specifies whether the event can bubble
     *  up the display list hierarchy.
     *
     *  @param cancelable Specifies whether the behavior
     *  associated with the event can be prevented.
     *
     *  @param kind The kind of changing event.  The valid values are defined in 
     *  GridSelectionEventKind class as constants.  This value 
     *  determines which properties in the event are used.
     * 
     *  @param rowIndex The zero-based index of the column that is being edited.
     * 
     *  @param columnIndex The zero-based index of the column that is being edited.
     * 
     *  @param column The column that is being edited.
     *   
     *  @see spark.components.gridClasses.GridColumn;
     * 
     */
    public function GridItemEditorEvent(type:String, 
                                      bubbles:Boolean = false, 
                                      cancelable:Boolean = false,
                                      rowIndex:uint = -1,
                                      columnIndex:uint = -1, 
                                      column:GridColumn = null)
    /** 
     *  The zero-based index of the column that is being edited.
     */ 
    public var columnIndex:int;

    /**
     *  The column of the cell that is being edited.
     */
    public var column:GridColumn;

    /**
     *  The index of the row that is being edited.
     */
    public var rowIndex:int;
}
}

GridItemEditor, DefaultGridItemEditor, ComboBoxGridItemEditor

GridItemEditor is a base class for IGridItemEditor implementations. All of the IGridItemEditor methods are essentially stubs except for the value property, validate() and save():

The value property represents the current value of the editor component(s). The value property is useful for the common scenario of editing a single value of a data provider. The data property setter is used to set the value property to datacolumn.dataField. Each subclass of GridItemEditor overrides the value setter to initialize its components. If you need to initialize your editor components from other than datacolumn.dataField, then override the data property setter.

The validate() method is responsible for verifying that the editor's value can be saved. If so it returns true, otherwise false. The default implementation of validate(), in the GridItemRenderer base class, verifies that each of the editor's top level IValidatorListener elements has not signalled an error.

The save() method can convert string values to/from numbers and booleans if necessary. The save() method gets the value of the editor component(s) from the value getter. By default the value is saved to datacolumn.dataField. If you need to update more than one data field then override the save() method.

package spark.components.gridClasses
{
public class GridItemEditor extends Group implements IGridItemEditor
{
}
}

An editor session is not closed when save() is called if the data is invalid per the validate() method. The editor session can always be canceled by calling DataGrid.endEditorSession(true). This is the default behavior and can be modified by overriding the save() method of GridItemEditor.

Note that in general GridItemEditor subclasses that only want to extend the base class's behavior, should call the super() methods to pick up any default behavior.

Two GridItemEditor subclasses are provided (defined in the spark.components.gridClasses package):

  • DefaultGridItemEditor.mxml: an GridItemEditor subclass that displays its value with a TextArea.
  • ComboBoxGridItemEditor.mxml: An GridItemEditor subclass that displays its value with a ComboBox. This class adds a dataProvider property which defines the list of elements displayed by the ComboBox.

Related

Wiki: Spark DataGrid

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.