Overview

The GridX cell editor system uses a factory-based registry that maps column types (BeepColumnType) to editor implementations. Seven default editors are pre-registered, covering all standard data types. The factory supports custom editor registration and a fallback mechanism for unknown column types.

Each editor wraps a native WinForms control (TextBox, ComboBox, CheckBox, etc.) and manages the full lifecycle: control creation, setup, value binding, event attachment, and cleanup. Editors communicate back to the grid via the IGridEditorEvents callback interface.

The system is managed by GridEditHelper (orchestrates edit mode enter/exit) and GridEditorFactory (69 lines, creates/resolves editor instances).

GridEditorFactory (69 lines)

GridEditorFactory maintains an internal Dictionary<BeepColumnType, Type> registry and provides three methods:

MethodSignatureDescription
Registervoid Register(BeepColumnType columnType, Type editorType)Registers a custom editor type for a column type; overrides the default
SetFallbackvoid SetFallback(Type editorType)Sets the fallback editor type (default: BeepGridTextEditor)
ResolveIGridEditor Resolve(BeepColumnType columnType)Creates and returns an editor instance; falls back if not registered

The factory is a singleton per grid instance. Resolve() uses Activator.CreateInstance() to instantiate editors, then calls editor.Setup() before returning. If no matching editor is found for a column type, the fallback editor is returned (defaults to BeepGridTextEditor).

Default Editor Registry

BeepColumnTypeEditor ClassNative Control
TextBeepGridTextEditorTextBox
ComboBoxBeepGridComboBoxEditorComboBox
DateTimeBeepGridDateDropDownEditorDateTimePicker
NumericUpDownBeepGridNumericEditorNumericUpDown
CheckBoxBoolBeepGridCheckBoxEditorCheckBox
CheckBoxCharBeepGridCheckBoxEditorCheckBox
CheckBoxStringBeepGridCheckBoxEditorCheckBox
MaskedTextBoxBeepGridMaskedEditorMaskedTextBox
ListBoxBeepGridGenericEditorTextBox
LOVBeepGridGenericEditorTextBox
ImageBeepGridGenericEditorTextBox
ButtonBeepGridGenericEditorTextBox
ProgressBarBeepGridGenericEditorTextBox
RadioBeepGridGenericEditorTextBox
Unknown / unregisteredBeepGridTextEditor (fallback)TextBox

IGridEditor Interface

Every editor implements IGridEditor. This contract defines the full editing lifecycle.

public interface IGridEditor
{
    Control CreateControl(BeepColumnConfig column, object currentValue);
    void Setup(BeepColumnConfig column, IGridEditorEvents editorEvents, Theme theme);
    void SetValue(object value);
    object GetValue();
    void AttachEvents();
    void DetachEvents();
    void OnBeginEdit(Rectangle cellBounds);
    bool IsPopupOpen { get; }
    bool IsEditing { get; }
    Control EditingControl { get; }
}
MethodDescription
CreateControlInstantiates the native WinForms control and configures it with column metadata
SetupStores column config, event callback, and theme references for the editing session
SetValueSets the current value into the editing control (called when edit mode begins)
GetValueRetrieves the (potentially modified) value from the editing control (called on commit)
AttachEventsWires up KeyDown, LostFocus, and value-changed events to the editor callback
DetachEventsUnwires all event handlers before disposing the control
OnBeginEditCalled when the cell enters edit mode; positions the control over the cell bounds
IsPopupOpenFor combo/date pickers: true while dropdown or calendar popup is visible

IGridEditorEvents

Editors communicate with the grid host via the IGridEditorEvents callback interface. The grid passes itself as this interface during Setup().

public interface IGridEditorEvents
{
    void RequestEndEdit(bool commit);
    void RequestCancelEdit();
    void OnEditorValueChanged(object newValue);
}
MethodDescription
RequestEndEdit(bool commit)Editor signals that editing should end. If commit=true, the current value is saved to the data source; if commit=false, changes are discarded
RequestCancelEditEditor signals that editing should be cancelled and the original value restored
OnEditorValueChangedOptional live-update: editor notifies grid of interim value changes (e.g., CheckBox toggle)

Editor Details

BeepGridTextEditor

Wraps a TextBox control. Enter key commits the edit (RequestEndEdit(true)). Escape cancels (RequestCancelEdit()). Supports MaxLength and CharacterCasing from column config. On LostFocus, auto-commits.

BeepGridComboBoxEditor

Wraps a ComboBox with DropDownStyle = DropDownList. Fills items from the column's DataSource or Items collection. Supports AutoCompleteMode = SuggestAppend when configured. Dropdown opens automatically on OnBeginEdit. Enter commits; Escape cancels.

BeepGridDateDropDownEditor

Wraps a DateTimePicker with ShowUpDown = false (calendar popup). Format from Format property (Short, Long, or custom). Popup opens on Focus. Enter or click-away commits; Escape cancels.

BeepGridNumericEditor

Wraps a NumericUpDown control. Reads Minimum, Maximum, Increment, and DecimalPlaces from column config. Supports ThousandsSeparator. Enter commits; Escape cancels. Up/down arrow keys adjust value within bounds.

BeepGridCheckBoxEditor

Wraps a CheckBox control supporting three data type mappings: bool (Checked/Unchecked), char (Y/N mapping), string (True/False mapping). On toggle, immediately fires RequestEndEdit(true) — no separate commit step needed. Space bar toggles the checkbox.

BeepGridMaskedEditor

Wraps a MaskedTextBox control. The mask is read from the column's Mask property (e.g., "(999) 000-0000"). PromptChar and TextMaskFormat configurable. Enter commits (must pass validation); Escape cancels.

BeepGridGenericEditor

Fallback editor for complex column types (ListBox, LOV, Image, Button, ProgressBar, Radio). Wraps a simple TextBox for string editing. Displays the cell's ToString() representation. Used when no specialized editor is registered.

Editor Lifecycle

The editing lifecycle is managed by GridEditHelper and follows this sequence:

1. User double-clicks cell or presses F2
2. GridEditHelper calls GridEditorFactory.Resolve(column.ColumnType)
3. Editor.CreateControl(column, currentValue) → instantiates native control
4. Editor.Setup(column, gridAsEvents, theme) → stores references
5. Editor.SetValue(currentValue) → populates control
6. Editor.AttachEvents() → wires KeyDown/LostFocus
7. Editor.OnBeginEdit(cellBounds) → positions & shows control
8. [User edits value]
9a. Enter pressed / focus lost → Editor calls events.RequestEndEdit(true)
9b. Escape pressed → Editor calls events.RequestCancelEdit()
10. Editor.GetValue() → grid writes back to data source
11. Editor.DetachEvents() → unwires handlers
12. Editor.EditingControl.Dispose() → cleanup

Examples

Custom Editor Registration

var factory = grid.EditorFactory;

factory.Register(BeepColumnType.DateTime, typeof(MyCustomDateEditor));

public class MyCustomDateEditor : IGridEditor
{
    private DateTimePicker _control;
    private IGridEditorEvents _events;

    public Control CreateControl(BeepColumnConfig column, object currentValue)
    {
        _control = new DateTimePicker
        {
            Format = DateTimePickerFormat.Custom,
            CustomFormat = "ddd, MMM dd yyyy",
            Width = 200
        };
        return _control;
    }

    public void Setup(BeepColumnConfig column, IGridEditorEvents editorEvents, Theme theme)
    {
        _events = editorEvents;
    }

    public void SetValue(object value) => _control.Value = (DateTime)value;
    public object GetValue() => _control.Value;
    public void AttachEvents()
    {
        _control.KeyDown += (s, e) =>
        {
            if (e.KeyCode == Keys.Enter) _events.RequestEndEdit(true);
            if (e.KeyCode == Keys.Escape) _events.RequestCancelEdit();
        };
    }
    public void DetachEvents() { }
    public void OnBeginEdit(Rectangle cellBounds)
    {
        _control.Bounds = cellBounds;
        _control.Focus();
        _control.Select();
    }
    public bool IsPopupOpen => false;
    public bool IsEditing => _control != null;
    public Control EditingControl => _control;
}

Set Fallback Editor

grid.EditorFactory.SetFallback(typeof(BeepGridMaskedEditor));