This question appears to have been answered already, possibly by MonkeyStyler/Mike Sutton, however, as I am using Delphi 10 Seattle, provided code and guides don\'t wor
You need to do quite much. Let's start.
At first you need declare some data type that will store the values for your ComboBox
column.
TComboRecord = record
FieldValues: array of string;
ItemSelected: integer;
function Selected: string;
end;
...
{ TComboRecord }
function TComboRecord.Selected: string;
begin
Result := FieldValues[ItemSelected];
end;
and populate a TList<TComboRecord>
with some data.
var
ComboData: TList<TComboRecord>;
procedure PopulateComboData(Rows: cardinal);
implementation
procedure PopulateComboData(Rows: cardinal);
var
RowI: cardinal;
i: cardinal;
ComR: TComboRecord;
begin
for RowI := 1 to Rows do
begin
Setlength(ComR.FieldValues, random(5) + 1);
for i := 0 to length(ComR.FieldValues) - 1 do
ComR.FieldValues[i] := inttostr(random(64000));
ComR.ItemSelected := 0;
ComboData.Add(ComR);
end;
end;
initialization
ComboData := TList<TComboRecord>.Create;
finalization
ComboData.Free;
Than you need create a TComboBox
ascendant so that it could store and manipulate the TComboRecord
type data.
TComboBoxCell = class(TComboBox)
private
FComboData: TComboRecord;
procedure SetComboData(const Value: TComboRecord);
function GetComboData: TComboRecord;
protected
procedure SetData(const Value: TValue); override;
public
property ComboData: TComboRecord read GetComboData write SetComboData;
end;
...
{ TComboBoxCell }
function TComboBoxCell.GetComboData: TComboRecord;
begin
FComboData.ItemSelected:=ItemIndex;
result:=FComboData;
end;
procedure TComboBoxCell.SetComboData(const Value: TComboRecord);
var
s: string;
begin
FComboData := Value;
Items.Clear;
for s in Value.FieldValues do
Items.Add(s);
ItemIndex := Value.ItemSelected;
end;
procedure TComboBoxCell.SetData(const Value: TValue);
begin
inherited;
ComboData := Value.AsType<TComboRecord>
end;
Than you need to inherit a new class form TColumn
:
TComboColumn = class(TColumn)
protected
procedure DoComboChanged(Sender: TObject);
function Grid: TComboExtendedGrid; overload;
function CreateCellControl: TStyledControl; override;
end;
...
{ TComboColumn }
function TComboColumn.CreateCellControl: TStyledControl;
begin
Result := TComboBoxCell.Create(Self);
TComboBoxCell(Result).OnChange := DoComboChanged;
end;
procedure TComboColumn.DoComboChanged(Sender: TObject);
var
P: TPointF;
LGrid: TComboExtendedGrid;
begin
LGrid := Grid;
if not Assigned(LGrid) then
Exit;
if FUpdateColumn then
Exit;
if FDisableChange then
Exit;
P := StringToPoint(TFmxObject(Sender).TagString);
LGrid.SetValue(Trunc(P.X), Trunc(P.Y),
TValue.From<TComboRecord>(TComboBoxCell(Sender).ComboData));
if Assigned(LGrid.FOnEditingDone) then
LGrid.FOnEditingDone(Grid, Trunc(P.X), Trunc(P.Y));
end;
function TComboColumn.Grid: TComboExtendedGrid;
var
P: TFmxObject;
begin
Result := nil;
P := Parent;
while Assigned(P) do
begin
if P is TCustomGrid then
begin
Result := TComboExtendedGrid(P);
Exit;
end;
P := P.Parent;
end;
end;
You see that now we have to subtype TGrid
class as well as we have to get its handler by Grid
function and need access to protected
FOnEditingDone
variable
TComboExtendedGrid = class(TGrid)
private
FOnEditingDone: TOnEditingDone;
protected
procedure SetValue(Col, Row: integer; const Value: TValue); override;
end;
{ TComboExtendedGrid }
procedure TComboExtendedGrid.SetValue(Col, Row: integer; const Value: TValue);
begin
inherited;
end;
Finally we need set the necessary creation and event handliing mechanism in our form unit. Add a column variable to the from declaration.
protected
CCColumn:TComboColumn;
populate the ComboData and create the column:
procedure TForm1.Button1Click(Sender: TObject);
begin
PopulateComboData(Grid2.RowCount);
CCColumn:=TComboColumn.Create(Grid2);
CCColumn.Parent := Grid2;
CCColumn.Header := 'CB';
end;
and handle the events:
procedure TForm1.Grid2GetValue(Sender: TObject; const Col, Row: Integer;
var Value: TValue);
begin
case Col of
6{Combo Column Number}: Value:=TValue.From<TComboRecord>(ComboData[Row])
end;
end;
procedure TForm1.Grid2SetValue(Sender: TObject; const Col, Row: Integer;
const Value: TValue);
begin
case Col of
6{Combo Column Number}: ShowMessage(Value.AsType<TComboRecord>.Selected);
end;
end;
Do not forget to pass changes (if you need) to ComboData
list. The current handlers will not do this for you. I prefer making this in Grid2SetValue
event handler.
Here is the easy way to add a Combobox to a FMX TGrid. Granted, this solution has the same items in every combo, but that's all I can achieve right now.
type
TComboColumn = class(TPopupColumn)
function CreateCellControl: TStyledControl; override;
end;
...
function TComboColumn.CreateCellControl: TStyledControl;
var
ComR: TComboRecord;
i: Integer;
begin
Result := TComboBoxCell.Create(self);
// now they all have the same drop-down values
ComR := frmMain.ComboData[0];
for i := 0 to Length(ComR.FieldValues) do
TComboBoxCell(Result).Items.Add(ComR.FieldValues[i]);
end
... And just for continuity
procedure TfrmMain.FormCreate(Sender: TObject);
begin
AGrid := TComboExtendedGrid.Create(Self);
AGrid.Parent := Self;
AGrid.Align := TAlignLayout.Client;
AGrid.Options := AGrid.Options + [TGridOption.AlwaysShowEditor];
AGrid.OnGetValue := AGridGetValue;
AGrid.OnSetValue := AGridSetValue;
AGrid.RowCount := 5;
ComboData := TList<TComboRecord>.Create;
PopulateComboData(AGrid.RowCount);
CCColumn := TComboColumn.Create(AGrid);
CCColumn.Parent := AGrid;
CCColumn.Header := 'Combohere';
end;