GridX Cell Editors
Pluggable editing — 7 editor types (Text, Numeric, Date, Combo, CheckBox, Masked, Generic) with GridEditorFactory registry and IGridEditor/IGridEditorEvents contracts
Table of Contents
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:
| Method | Signature | Description |
|---|---|---|
Register | void Register(BeepColumnType columnType, Type editorType) | Registers a custom editor type for a column type; overrides the default |
SetFallback | void SetFallback(Type editorType) | Sets the fallback editor type (default: BeepGridTextEditor) |
Resolve | IGridEditor 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
| BeepColumnType | Editor Class | Native Control |
|---|---|---|
Text | BeepGridTextEditor | TextBox |
ComboBox | BeepGridComboBoxEditor | ComboBox |
DateTime | BeepGridDateDropDownEditor | DateTimePicker |
NumericUpDown | BeepGridNumericEditor | NumericUpDown |
CheckBoxBool | BeepGridCheckBoxEditor | CheckBox |
CheckBoxChar | BeepGridCheckBoxEditor | CheckBox |
CheckBoxString | BeepGridCheckBoxEditor | CheckBox |
MaskedTextBox | BeepGridMaskedEditor | MaskedTextBox |
ListBox | BeepGridGenericEditor | TextBox |
LOV | BeepGridGenericEditor | TextBox |
Image | BeepGridGenericEditor | TextBox |
Button | BeepGridGenericEditor | TextBox |
ProgressBar | BeepGridGenericEditor | TextBox |
Radio | BeepGridGenericEditor | TextBox |
Unknown / unregistered | BeepGridTextEditor (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; }
}| Method | Description |
|---|---|
CreateControl | Instantiates the native WinForms control and configures it with column metadata |
Setup | Stores column config, event callback, and theme references for the editing session |
SetValue | Sets the current value into the editing control (called when edit mode begins) |
GetValue | Retrieves the (potentially modified) value from the editing control (called on commit) |
AttachEvents | Wires up KeyDown, LostFocus, and value-changed events to the editor callback |
DetachEvents | Unwires all event handlers before disposing the control |
OnBeginEdit | Called when the cell enters edit mode; positions the control over the cell bounds |
IsPopupOpen | For 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);
}| Method | Description |
|---|---|
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 |
RequestCancelEdit | Editor signals that editing should be cancelled and the original value restored |
OnEditorValueChanged | Optional 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() → cleanupExamples
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));