2010. december 21., kedd

Create a Combobox winthin in a Stringgrid


Problem/Question/Abstract:

How to dynamically create a Combobox within a Cell of a StringGrid

Answer:

You need a descendent of TStringgrid that properly reflects WM_COMMAND to embedded controls. The standard grid does not do it since it is not intended to play parent to other controls.

Additionaly simply declare a Set- and GetMethod to access the items of die combobox

unit BWControlStringGrid;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  Grids, stdctrls;

type
  TBWControlStringGrid = class(TStringGrid)
  private
    fComboBox: TCombobox;
    procedure WMCommand(var msg: TWMCommand); message WM_COMMAND;
    procedure DblClick; override;
    procedure Click; override;
    procedure RelocateComboBox;
    procedure HideCombobox;
  protected
    procedure KeyPress(var Key: Char); override;
  public
    constructor Create(AOWner: TComponent); override;
    destructor Destroy; override;
  published
  end;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('hEaDRoOm', [TBWControlStringGrid]);
end;

procedure TBWControlStringGrid.WMCommand(var msg: TWMCommand);
begin
  if EditorMode and (msg.Ctl = fComboBox.Handle) then
    inherited
  else if msg.Ctl <> 0 then
    msg.result :=
      SendMessage(msg.ctl, CN_COMMAND,
      TMessage(msg).wparam,
      TMessage(msg).lparam);
end;

procedure TBWControlStringGrid.KeyPress(var Key: Char);
begin
  if Key = #13 then
    RelocateComboBox
  else
    HideCombobox;

end;

procedure TBWControlStringGrid.DblClick;
begin
  inherited;
  RelocateComboBox;
end;

procedure TBWControlStringGrid.Click;
begin
  inherited;
  HideCombobox;
end;

procedure TBWControlStringGrid.RelocateComboBox;
begin
  fcombobox.boundsrect := CellRect(Selection.Left, Selection.Top);
  fcomboBox.Visible := TRUE;
  fcombobox.setfocus;
end;

procedure TBWControlStringGrid.HideCombobox;
begin
  fcomboBox.Visible := false;
end;

constructor TBWControlStringGrid.Create(AOWner: TComponent);
begin
  inherited Create(Aowner);
  fComboBox := TComboBox.Create(self);
  fComboBox.Parent := self;
  fComboBox.Visible := FALSE;
  Options := Options - [goRangeSelect];
end;

destructor TBWControlStringGrid.Destroy;
begin
  fComboBox.Destroy;
  inherited destroy;
end;

end.

This is great, but is just the skeleton, of course..

There needs to be some mechansim for getting the combo's text/selection into the cell, also for relaying the cells contents into the combo in the first place.

This can be done in the Hide and Relocate methods.

The whole thing can get unwieldy if you add a lot of get/set methods for updating the combos dropdownlist, etc, so making the Combo a Public property, rather than just a private field might help with that - the onus is then on the programmer to deal with the combo directly - it is unlikely, for instance, that the dropdownlist would be the same for each column.

Or two new events could be triggered - OnHide and OnRelocate
eg:

TComboVisibleChangeEvent = procedure(Sender: TObject; Row, Col: Longint; Combo:
  TComboBox; AllowVisibleChange: boolean) of object

fOnHide: TComboVisibleChangeEvent;
fOnRelocate: TComboVisibleChangeEvent;

etc.

This way the Combo would be make public when needed. When the Relocate fires, the dropdownlist could be repopulated, etc

Just ideas for whoever wants them!

Nincsenek megjegyzések:

Megjegyzés küldése