TAdvStringGrid

Example 24 : using other TMS edit controls as inplace editors in TAdvStringGrid

vcl grid inplace editors

Since v1.90 of TAdvStringGrid, using other inplace editors than those built-in in TAdvStringGrid is possible. This is achieved through a new introduced component TEditLink which takes care of the communication of your edit control and the grid. In order to use another inplace editor, it is sufficient to write an EditLink descendant with some methods that do the specific communication with your edit control. The only requirement is that your edit control is descendant from TWinControl which should not be a problem since almost all are.

In depth look at the TEditLink component:

TEditLink = class(TComponent)
public
  constructor Create(aOwner:TComponent); override;
  destructor Destroy; override;
  procedure EditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  property EditCell:TPoint;
  property Grid:TAdvStringGrid;
  procedure HideEditor;
  function GetCellValue:string;
  procedure SetCellValue(s:string);
  procedure CreateEditor(aParent:TWinControl); virtual;
  procedure DestroyEditor; virtual;
  procedure SetFocus(value:boolean); virtual;
  procedure SetRect(r:trect); virtual;
  procedure SetVisible(value:boolean); virtual;
  procedure SetProperties; virtual;
  function GetEditControl:TWinControl; virtual;
  function GetEditorValue:string; virtual;
  procedure SetEditorValue(s:string); virtual;
published
  property EditStyle:TEditStyle;
  property PopupWidth:integer;
  property PopupHeight:integer;
  property WantKeyLeftRight:boolean;
  property WantKeyUpDown:boolean;
  property WantKeyHomeEnd:boolean;
  property WantKeyPriorNext:boolean
  property WantKeyReturn:boolean;
  property WantKeyEscape:boolean;
  property Tag:integer;
end;

The EditLink presents a series of virtual methods, properties and helper functions that can be used to communicate with your edit control. You can override these virtual methods where the default behaviour of the TEditLink must be changed. Below is a discussion of each of these virtual methods :

procedure CreateEditor(aParent:TWinControl);

Override this method to create an instance of your edit control. Assign the aParent parameter to its Parent property. In this stage, the edit control should still be invisible.

It is necessary to override this method.

procedure DestroyEditor;

Override this method to free the instance of your inplace editor after editing. It is necessary to override this method.

procedure SetFocus(value:boolean);

Override this method only if a special action is required at the time your edit control receives or looses focus. Overriding this method is normally not required.

procedure SetRect(r:trect);

With this method the coordinates and size is set for the inplace edit control to fit in the cell where inplace editing happens. An override of this method should only be necessary when your inplace edit control does not fit into the cell itself, like for example a combobox that drops out of the cell. In this case, you can just set the height of the edit control in the SetRect method.

procedure SetVisible(value:boolean);

Override this method only if a special action is required at the time your edit control is made visible or is hidden again. Overriding this method is normally not required.

procedure SetProperties(value:boolean);

Override this method if properties of the edit control must be set after it is visible. Some edit control properties only work properly when set when the edit control is visible. In this case, the SetProperties method is the ideal place.

function GetEditControl:TWinControl;

Override this method to return your edit control as TWinControl. Your edit control should be descendant of TWinControl so you can cast it to a TWinControl. For example : result:=TWinControl(myEdit);

It is necessary to override this method.

function GetEditorValue:string;

Override this function to return the value of your edit control as a string to put into the cell after editing.

It is necessary to override this method.

procedure SetEditorValue(s:string);

Override this method to set the value of your edit control from the current cell value before editing. It is necessary to override this method.

Further, there are some helper functions :

procedure HideEditor;

Hides the inplace edit control. This method should be called when your edit control looses focus. It is typically called from your edit control OnExit event.

procedure EditKeyDown;

Default key handler for special keys that are used inside the grid, such as arrow keys, return key etc..

function GetCellValue:string;

Retrieves the cell value of the cell being edited. Normally this is not used, but done through the SetEditorValue method.

procedure SetCellValue(s:string);

Sets the cell value of the cell being edited. Normally this is not used, but done through the GetEditorValue method.

The EditLink properties are :

property EditStyle:TEditStyle;

Determines if your edit control is esInplace or esPopup style. Specify esPopup style only for inplace edit control that can fully overlap the grid, for example when using a TMemo that could hang out of the grid during editing. All other edit control, including a combobox should be declared as esInplace since their main editing part stays inside the grid's cell.

property PopupWidth:integer;

Defines the width of the overlapping edit control in esPopup style.

property PopupHeight:integer;

Defines the height of the overlapping edit control in esPopup style.

property WantKeyXXXX:boolean;

Defines if the edit control handles the key itself or the grid's default key handler should handle the key. For multiline inplace editors for example, it might be necessary to let your edit control handle the return key itself instead of the grid.

property Tag:integer;

Property that can be used to further identify your EditLink descendant.

property Grid:TAdvStringGrid;

Returns the grid being edited.

property EditCell:TPoint;

Returns the coordinates of the cell being edited.

Using the TEditLink with TAdvStringGrid

After the TEditLink descendant has been written to communicate with your edit control, it is necassary to tell TAdvStringGrid to use this EditLink component and thus also your edit control. To achieve this, the TAdvStringGrid's EditLink property is used with the OnGetEditorType event. In the OnGetEditorType event, the inplace editor is defined as edCustom and either globally or in this event, the EditLink property of TAdvStringGrid can be set to your descendant TEditLink. Of course, when the grid's EditLink property is set globally, only one custom inplace editor type can be used, but when it is set from the OnGetEditorType event, nothing prevents you from writing multiple TEditLink descendant components and assign them dependent on which cells you want to edit in the grid. As such, a typical OnGetEditorType event could look like:

procedure TForm1.AdvStringGrid1GetEditorType(Sender: TObject; aCol,
  aRow: Integer; var aEditor: TEditorType);
begin
  case acol of
  2: advstringgrid1.EditLink:=EditLink1;
  3: advstringgrid1.EditLink:=EditLink2;
  4: advstringgrid1.EditLink:=EditLink3;
  5: advstringgrid1.EditLink:=EditLink4;
  6: advstringgrid1.EditLink:=EditLink5;
  end;
  if acol in [2,3,4,5,6] then aEditor:=edCustom;
end;

Here, 5 different EditLink types have been used to use a different inplace editor for 5 different columns. As your edit control will not have been constructed yet in the OnGetEditorType event, this is not a good place to specify properties of your edit control dependent of the position of the edit control in the grid. Although this is usually not necessary, it can be interesting for example to change your edit control's color or font depending on the color or font of the cell being edited. This can be achieved in the OnGetEditorProp event which is called after your edit control has been constructed with help of the EditLink specified. In the example below, a TAdvEdit control is used as inplace editor and the focus color is adapted to the banding color used in the grid:

procedure TForm1.AdvStringGrid1GetEditorProp(Sender: TObject; aCol, aRow: Integer; aEditLink: TEditLink);
begin
  if acol=2 then
  begin
    if odd(arow) then 
      (aEditLink.GetEditControl as TAdvEdit).FocusColor:=clInfoBk 
    else
      (aEditLink.GetEditControl as TAdvEdit).FocusColor:=clWhite;
  end;
end;

Example of a TEditLink to use TAdvEdit in TAdvStringGrid (minimal implementation)

type
TAdvEditEditLink = class(TEditLink)
private
  fEdit:TAdvEdit;
protected
  procedure EditExit(Sender:TObject);
public
  procedure CreateEditor(aParent:TWinControl); override;
  procedure DestroyEditor; override;
  function GetEditorValue:string; override;
  procedure SetEditorValue(s:string); override;
  function GetEditControl:TWinControl; override;
end;

To link TAdvEdit with TAdvStringGrid, only a miminum set of TEditLink methods are used:

In the CreateEditor method, the TAdvEdit instance is created, its parent is set, the OnKeyDown event is assigned to the default EditKeyDown handler, size is set to 0 to make sure it is always invisible, some properties like ModifiedColor, ShowModified and BorderStyle are set. Finally, since TAdvEdit should handle the the Left, Right arrow keys as well as Home & End keys, the properties WantKeyLeftRight and WantKeyHomeEnd are set accordingly:

{ TAdvEditEditLink }

procedure TAdvEditEditLink.CreateEditor(aParent:TWinControl);
begin
  fEdit:=TAdvEdit.Create(Grid);
  fEdit.BorderStyle := bsNone;
  fEdit.OnKeydown:= EditKeyDown;
  fEdit.OnExit := EditExit;
  fEdit.Width:=0;
  fEdit.Height:=0;
  fEdit.Parent:=aParent;
  WantKeyLeftRight:=true;
  WantKeyHomeEnd:=true;
end;

The DestroyEditor simple frees the instance of the inplace editor:

procedure TAdvEditEditLink.DestroyEditor;
begin
  if assigned(fEdit) then
    fEdit.Free;
  fEdit:=nil;
end;

Since the TAdvEdit component works with strings as well to edit, the GetEditorValue and SetEditorValue methods are simply setting and getting the cell contents to and from the TAdvEdit component's Text property:

function TAdvEditEditLink.GetEditorValue:string;
begin
  Result := fEdit.Text;
end;

procedure TAdvEditEditLink.SetEditorValue(s: string);
begin
  fEdit.Text := s;
end;

In order to hide the editor when it looses focus, the EditExit procedure for the OnExit event, calls the HideEditor method:

procedure TAdvEditEditLink.EditExit(sender: TObject);
begin
  HideEditor;
end;

Finally, much of the magic behind the TEditLink works because TAdvStringGrid treats the inplace editor as a TWinControl descendant, and therefore the grid must be able to obtain it as such with the GetEditControl method:

function TAdvEditEditLink.GetEditControl: TWinControl;
begin
  Result:=fEdit;
end;

Making more edit control properties available at design time

This was the minimal implementation of the TEditLink that uses the TAdvEdit component with its default properties. To make the TAdvEdit properties accessible at design time, the TAdvEdit properties can be added to the TEditLink component and transferred from the TEditLink component to the TAdvEdit component in the SetProperties method. In the TAdvEditEditLink component provided this is done in following way:

TAdvEditEditLink = class(TEditLink)
public
  procedure SetProperties; override;
published
  property EditAlign:TEditAlign read fEditAlign write fEditAlign;
  property EditColor:TColor read fEditColor write fEditColor;
  property ModifiedColor:TColor read fModifiedColor write fModifiedColor;
  property EditType:TAdvEditType read fEditType write fEditType;
  property Prefix:string read fPrefix write fPrefix;
  property ShowModified:boolean read fShowModified write fShowModified;
  property Suffix:string read fSuffix write fSuffix;
  property Precision:integer read fPrecision write fPrecision;
end;

The set of properties that is exposed with the TEditLink is used for TAdvEdit in the SetProperties method:

procedure TAdvEditEditLink.SetProperties;
begin
  inherited;
  fEdit.Color := fEditColor;
  fEdit.FocusColor := fEditColor;
  fEdit.EditAlign := fEditAlign;
  fEdit.ModifiedColor := fModifiedColor;
  fEdit.Prefix := fPrefix;
  fEdit.Suffix := fSuffix;
  fEdit.ShowModified := fShowModified;
  fEdit.Precision := fPrecision;
end;

In the full demo that can be downloaded here, EditLinks are used for TAdvEdit, TMoneyEdit, a memo inplace editor, a TCheckListEdit inplace editor and a TColorCombo inplace editor. Make sure you have these components installed when compiling the demo.

Delphi project & source files for downloading included in the main demos distribution for Delphi.

In order to run the sample project, make sure TAdvEdit, TMoneyEdit, TCheckListEdit are installed as well then install the editlinks with the files ASGLINKS.PAS and COLORCOMBO.PAS