Back to FireMonkey Topics
In Custom Grid Columns we saw how to create a column for a control class. Here I’m going to build on that to demonstrate a column with both a custom cell control class and custom column class. This shows how to create columns of custom components.
In this example I’ll take a standard TTextCell (which you’ll remember is descended from TEdit) and format it’s contents as currency. For this I shall: right align the text; display it’s contents as a monetary value; show negative values in red.
The Class Declarations
We need two classes, one for the cells and another for the column. Here are the class declarations,
type TMoneyCell = class(TTextCell)
private
FFloatValue: Single;
protected
procedure SetData(const Value: TValue);override;
public
constructor Create(Owner: TComponent);override;
end;
type TMoneyColumn = class(TColumn)
protected
function CreateCellControl: TStyledControl;override;
end;
TMoneyCell
In the cell’s constructor I firstly remove ssFontColor from StyledSettings so I can override the font color from the style. Next I set the TextAlign to taTrailing, which right-aligns the contents.
constructor TMoneyCell.Create(Owner: TComponent);
begin
inherited;
StyledSettings := StyledSettings-[TStyledSetting.ssFontColor];
TextAlign := TTextAlign.taTrailing;
end;
A grid sets each cell’s contents by setting the Data property of the cell, which uses the SetData setter method, so I override that to handle setting the value.
procedure TMoneyCell.SetData(const Value: TValue);
begin
FFloatValue := Value.AsType<Single>;
inherited SetData(Format('%m', [FFloatValue]));
if FFloatValue < 0 then
FontColor := claRed
else
FontColor := claBlack;
end;
This method starts by extracting the data from the TValue, which is stored as a single. I then format it appropriately and pass it on to the inherited SetData method from the TEdit to be displayed as usual.
Finally I set the FontColor to reflect whether the value is positive or negative.
Note here that I haven’t implemented GetData which will be called to read changed cell values. This is left as an exercise for the reader, if you want editable cells.
TMoneyColumn
For the column I only need to override CreateCellControl so I can create my cell and set a few event handlers which pass events back to the application via the grid.
function TMoneyColumn.CreateCellControl: TStyledControl;
begin
Result := TMoneyCell.Create(Self);
TMoneyCell(Result).OnTyping := DoTextChanged;
TMoneyCell(Result).OnChange := DoTextChanged;
TMoneyCell(Result).OnExit := DoTextExit;
end;
On the Form
Now we need to use our new column. You’ll recall that there’s no way to register custom column types with the IDE for use at design time so you’ll need to add the column in code,
procedure TForm1.FormCreate(Sender: TObject);
begin
Grid1.AddObject(TMoneyColumn.Create(Grid1));
end;
And I test it with some random sample data,
procedure TForm1.Grid1GetValue(Sender: TObject; const Col, Row: Integer;
var Value: TValue);
begin
if Col = 0 then
Value := TValue.From<Single>(Random(10000)/100-50);
end;
Source
Here’s the full source of the MoneyColumn unit:
unit MoneyColumn;
interface
uses FMX.Grid, System.RTTI, FMX.Controls, Classes;
type TMoneyCell = class(TTextCell)
private
FFloatValue: Single;
protected
procedure SetData(const Value: TValue);override;
public
constructor Create(Owner: TComponent);override;
end;
type TMoneyColumn = class(TColumn)
protected
function CreateCellControl: TStyledControl;override;
end;
implementation
uses FMX.Types, SysUtils, FMX.Objects, System.UIConsts;
{ TMoneyColumn }
function TMoneyColumn.CreateCellControl: TStyledControl;
begin
Result := TMoneyCell.Create(Self);
TMoneyCell(Result).OnTyping := DoTextChanged;
TMoneyCell(Result).OnChange := DoTextChanged;
TMoneyCell(Result).OnExit := DoTextExit;
end;
{ TMoneyCell }
constructor TMoneyCell.Create(Owner: TComponent);
begin
inherited;
StyledSettings := StyledSettings-[TStyledSetting.ssFontColor];
TextAlign := TTextAlign.taTrailing;
end;
procedure TMoneyCell.SetData(const Value: TValue);
begin
FFloatValue := Value.AsType<Single>;
inherited SetData(Format('%m', [FFloatValue]));
if FFloatValue < 0 then
FontColor := claRed
else
FontColor := claBlack;
end;
end.