VCL Delphi

Title: Replacing the TStringGrid's standard InplaceEditor
Question: How can I modify a TStringGrid to have a picklist or an ellipsis editor button in selected cells?
Answer:
Replacing the TStringGrid's standard InplaceEditor
The TStringGrid is a widely used Delphi component. It is possible to enter text directly into the grid cells, but it is sometimes desirable to use alternative inplace editors, such as a combobox to select from a picklist of values, or a user dialog called by clicking on an ellipsis button in the corresponding grid cell. While this is a standard behavior for a TDBGrid, it is not available for the non-data aware TStringGrid. Some solutions can be found all over the web, but the basic functionality, however, has been implemented already in the ancestor of any grid, TCustomGrid. I'm working with Delphi 6, but I think this schould be true also for older versions.
The corresponding inplace editor which has all the features mentioned can be found in the Grids unit as TInplaceEditList. It can be accessed by inheriting from TStringGrid a new component which will be named TNewStringGrid here. In this new component, the method "CreateEditor" is overridden to use the TInplaceEditList as inplace editor (instead of the standard TInplaceEdit). The new stringgrid also inherits the method "GetEditStyle" which decides which cells will use the new inplace editor.
In the implementation of TNewStringGrid, we create a new event "OnGetEditStyle" which calls the GetEditStyle method. The event handler receives the cell coordinates and returns the editor style (esSimple, esPickList or esEllipsis, as defined in grids.pas).
Additionally for the esPicklist editor style, we have to write an event handler for the "OnGetPickListItems" event to pass the pick list (a TStringList) to the new string grid. For the esEllipsis editor style, we need an event handler for the event "OnEditButtonClicked" which is fired when the user clicks on the new ellipsis button; this event handler for example could open a complex dialog querying user input and should fill the cell with the corresponding text entered.
Don't forget to activate the goEditing option of the stringgrid; otherwise you won't be able to edit the grid data.
-------------------------------------------------------------------------------
What follows is the source of TNewStringGrid unit:
unit NewStringGrid;
interface
uses
Windows, Messages, SysUtils, Classes, Controls, Grids;
type
TGetEditStyleEvent = procedure (TSender:TObject; ACol,ARow:integer;
var EditStyle:TEditStyle) of object;
TNewStringGrid = class(TStringGrid)
private
FDropdownRowCount : integer;
FOnEditButtonClick : TNotifyEvent;
FOnGetEditStyle : TGetEditStyleEvent;
FOnGetPickListItems : TOnGetPickListItems;
procedure SetDropdownRowCount(value:integer);
procedure SetOnEditButtonClick(value:TNotifyEvent);
procedure SetOnGetPicklistItems(value:TOnGetPickListItems);
protected
function CreateEditor: TInplaceEdit; override;
function GetEditStyle(ACol, ARow: integer): TEditStyle; override;
public
constructor Create(AOwner:TComponent); override;
published
property DropdownRowCount : integer
read FDropDownRowCount write SetDropdownRowCount default 8;
property OnEditButtonClick: TNotifyEvent
read FOnEditButtonClick write SetOnEditButtonClick;
property OnGetEditStyle : TGetEditStyleEvent
read FOnGetEditStyle write FOnGetEditStyle;
property OnGetPickListItems : TOnGetPickListItems
read FOnGetPickListItems write SetOnGetPickListItems;
end;
procedure Register;
implementation
constructor TNewStringGrid.Create(AOwner:TComponent);
begin
inherited Create(AOwner);
FDropdownRowCount := 8;
end;
function TNewStringGrid.CreateEditor: TInplaceEdit;
begin
result := TInplaceEditList.Create(self);
with TInplaceEditList(result) do begin
DropdownRows := FDropdownRowCount;
OnGetPickListItems := FOnGetPickListItems;
OnEditButtonClick := FOnEditButtonClick;
end;
end;
function TNewStringGrid.GetEditStyle(ACol,ARow:integer) : TEditStyle;
begin
result := esSimple;
if Assigned(FOnGetEditStyle)
then FOnGetEditStyle(self, ACol, ARow, result);
end;
procedure TNewStringGrid.SetDropDownRowCount(value:integer);
begin
FDropdownRowCount := value;
if Assigned(InplaceEditor)
then TInplaceEditList(InplaceEditor).DropdownRows := value;
end;
procedure TNewStringGrid.SetOnEditButtonClick(value:TNotifyEvent);
begin
FOnEditButtonClick := value;
if Assigned(InplaceEditor)
then TInplaceEditList(InplaceEditor).OnEditButtonClick := value;
end;
procedure TNewStringGrid.SetOnGetPicklistItems(value:TOnGetPicklistItems);
begin
FOnGetPicklistItems := value;
if Assigned(InplaceEditor)
then TInplaceEditList(InplaceEditor).OnGetPickListitems := value;
end;
procedure Register;
begin
RegisterComponents('Additional', [TNewStringGrid]);
end;
end.
-------------------------------------------------------------------------------
Here is a little demo project:
unit TestUnit;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Grids, NewStringGrid;
type
TForm1 = class(TForm)
NewStringGrid1: TNewStringGrid;
procedure FormCreate(Sender: TObject);
procedure NewStringGrid1GetEditStyle(TSender: TObject; ACol,
ARow: Integer; var EditStyle: TEditStyle);
procedure NewStringGrid1GetPickListItems(ACol, ARow: Integer;
Items: TStrings);
procedure NewStringGrid1EditButtonClick(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private-Deklarationen }
PickList : TStringList;
public
{ Public-Deklarationen }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
var
i,r,c : integer;
const
n = 10;
begin
with NewStringGrid1 do begin
for r:=1 to RowCount-1 do Cells[0,r] := IntToStr(r);
for c:=1 to ColCount-1 do Cells[c,0] := char(ord('A')+c-1);
end;
PickList := TStringList.Create;
for i:=1 to n do PickList.Add(Format('Item %d', [i]));
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
PickList.Free;
end;
procedure TForm1.NewStringGrid1GetEditStyle(TSender: TObject;
ACol,ARow: Integer; var EditStyle: TEditStyle);
begin
if ACol=1 then EditStyle := esPickList;
if ACol=2 then EditStyle := esEllipsis;
end;
procedure TForm1.NewStringGrid1GetPickListItems(ACol, ARow: Integer;
Items: TStrings);
begin
Items.Assign(PickList);
end;
procedure TForm1.NewStringGrid1EditButtonClick(Sender: TObject);
var
s : string;
begin
with NewStringGrid1 do
Cells[Col,Row] := Inputbox('Enter your data', 'Data:', Cells[Col,Row]);
end;
end.
-----------------------------------------------
object Form1: TForm1
Left = 200
Top = 107
Width = 416
Height = 303
Caption = 'Test Form'
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
OldCreateOrder = False
OnCreate = FormCreate
OnDestroy = FormDestroy
PixelsPerInch = 96
TextHeight = 13
object NewStringGrid1: TNewStringGrid
Left = 0
Top = 0
Width = 408
Height = 276
Align = alClient
ColCount = 10
Options = [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine, goRangeSelect, goEditing, goAlwaysShowEditor]
TabOrder = 0
OnEditButtonClick = NewStringGrid1EditButtonClick
OnGetEditStyle = NewStringGrid1GetEditStyle
OnGetPickListItems = NewStringGrid1GetPickListItems
end
end