While porting my VCL applications to FireMonkey I needed to find a replacement for the TAdvStringGrid from TMS software and so decided to try building my own custom TStringGrid. I was only looking for a stop gap minimal implementation as I expect (hope) that TMS will soon deliver FireMonkey versions of their grid components.
I used the example provided by Embarcadero as a guide to the component design and the TIsAdvStringGrid was born. In this instance I did not require to change the style handling of the base component so the style discussion of the document was ignored and no GetStyleObject function provided.
The full code is available in the zip of my converter download but the main points are
Step one: Create and Object Interface section inheriting from a current FMX Object (in this case TStringGrid) and add the extra properties and functionality required.
TISAdvStringGrid = class(TStringGrid)
private
>>
protected
{ Protected declarations }
//function GetStyleObject: TControl; override;
public
{ Public declarations }
function FindFirst(ASearchText: String; AMatchStates: TFindParams) : TGridCell;
function ObjectSelected:TObject;
procedure ClearAll;
procedure AutoSizeColumns(ADoFixed: Boolean; APadding: Real);
procedure AutoSizeRows(ADoFixed: Boolean; APadding: Real);
published
{ Published declarations }
property ColumnCount: integer read GetColumnCount write SetColumnCount;
property OneDObjectArray: TArrayofObjects read FOneDObjectArray write SetOneDObjectArray;
property TwoDObjectArray: TTwoDArrayofObjects read FTwoDObjectArray write SetTwoDObjectArray;
end;
Step Two: Register the New Object
procedure Register;
implementation
uses
Types;
procedure Register;
begin
RegisterComponents('Innova Solutions', [TISAdvStringGrid]);
end;
Step Three: Fill Out the New Functions and Properties
In my case the AutoSizeColumns and AutoSizeRows functions were dummies provided as stubs to maintain other code until TMS (hopefully) provide the real thing. SetColumnCount and ClearAll however required an insight into the differences between a VCL TStringGrid and the very different FireMonkey Version.
{ TISAdvStringGrid }
function TISAdvStringGrid.GetColumnCount: integer;
begin
Result := inherited ColumnCount;
end;
function TISAdvStringGrid.ObjectSelected: TObject;
Var
Row,Col:integer;
ObjMaxRow,ObjMaxCol:Integer;
begin
Result:=nil;
ObjMaxCol:=-1;
ObjMaxRow:=High(FTwoDObjectArray);
if ObjMaxRow<0 then
ObjMaxRow:=High(FOneDObjectArray)
Else
ObjMaxCol:=High(FTwoDObjectArray[0]);
if ObjMaxRow<0 then Exit;
Row:=Selected;
If Row<0 then exit;
if Row>ObjMaxRow then exit;
if ObjMaxCol<0 then
Result:=FOneDObjectArray[Row]
else
begin
Col:=ColumnIndex;
if Col<0 then exit;
if Col>ObjMaxCol then Exit;
Try
Result:=FTwoDObjectArray[Row,Col];
Except
Result:=nil;
End;
end;
end;
procedure TISAdvStringGrid.SetColumnCount(const Value: integer);
Var
i, CurrentCount: integer;
NxtColunm: TStringColumn;
begin
CurrentCount := ColumnCount;
if Value < CurrentCount then
For i := CurrentCount - 1 Downto Value - 1 do
ColumnByIndex(i).Free
else
For i := CurrentCount + 1 to Value do
begin
NxtColunm := TStringColumn.Create(Self);
AddObject(NxtColunm);
end;
end;
procedure TISAdvStringGrid.AutoSizeColumns(ADoFixed: Boolean; APadding: Real);
begin
//Dummy
end;
procedure TISAdvStringGrid.AutoSizeRows(ADoFixed: Boolean; APadding: Real);
begin
//Dummy
end;
procedure TISAdvStringGrid.ClearAll;
var
i: integer;
Obj: TFmxObject;
begin
if (FContent <> nil) and (FContent.ChildrenCount > 0) then
for i := FContent.ChildrenCount - 1 Downto 0 do
if FContent.Children[i] is TColumn then
begin
Obj := FContent.Children[i];
FContent.RemoveObject(Obj);
Obj.Free;
end;
end;
Step Four: Include in a Package File and install in the IDE
requires rtl, fmx; contains IsAdvStringGrid in 'IsAdvStringGrid.pas'
There were no instructions on adding the component graphic to use on the pallet. I did make a token effort using what I recalled from doing VCL components years ago but had no success and it seems less important with the current pallet layout.
What about a screenshot?
Many thanks for posting this – good to have such a decent walk-through on extending an existing FireMonkey control.
I think your blog somehow ate the private section of the class declaration.
Sorry I tried to keep only the interesting bits in the blog.
Full source is available at http://www.innovasolutions.com.au/delphistuf/ADUGVCLtoFMXConv.htm
Roger, how to sort a Firemonkey TStringGrid? Could you help me, please?
I was hoping that TMS would have their grid out soon and I could let them do the sorting work but it seems to be at least six months away.
I would approach sorting by creating a TStringlist (SL) and setting its sort parameters to Sort, Allow All and the desired case sensitivity. If you then add all the text from the chosen column as SL.AddObject(Cell[Col,Row] ,TObject(Row)) where Row steps from FixedRows to highest Row you will then have the new order. Turn off sorting (SL.sorted:=false) and for each column put all Column data into the string list in the new order
SL[Row-FixedRows]:=Cell[Col,Integer(SL.Object[Row-FixedRows]]
then write it back
Cell[Col,Row]:= SL[Row-FixedRows].
If you had the object array shown in my code you would then need to resort that also.
But the Firemonkey StringGrid does not have a fixed rows concept yet and the TMS Grid allows you to sort up and sort down and revert back to the original order all driven by clicks on the fixed row. TMS grid also has so much more functionality I guess I can see why it might take some time just to decide how to approach designing a Firemonkey AdvancedStringGrid.