7

I am looking for recommended solution to style a TGrid cell that is being drawn by the OnGetValue call (that is called to paint the cells in view). For background, an excellent response by Mike, showed how to simply apply a tAlign property when the cell is created; but my next challenge is colouring the cell contents.

Previous posting/answer

The objective is to change the cell attributes (Font, style, colour etc...) of the value I am about to return as the cell "Value". In the example below; it would be applying a style to the OnGetValue "value" that is being returned. It may well be that we have to do this via a FM Stylesheet; or can we get directly to the TText attributes? Ideally, both scenarios would be great - but at this stage I will take either solution... (;->

unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.Objects, FMX.Grid,
  FMX.Layouts, FMX.Edit;

type
  TForm1 = class(TForm)
    Grid1: TGrid;
    Button1: TButton;
    StyleBook1: TStyleBook;
    procedure Grid1GetValue(Sender: TObject; const Col, Row: Integer;
      var Value: Variant);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

  TStringColNum = class(TStringColumn)
  private
    function CreateCellControl: TStyledControl; override;
  published
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

function TStringColNum.CreateCellControl: TStyledControl;
begin
  Result:=TTextCell.Create(Self);
  TTextCell(Result).TextAlign := TTextAlign.taTrailing;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  Grid1.AddObject(TStringColumn.Create(Self));
  Grid1.AddObject(TStringColNum.Create(Self)); // Right Aligned column?

  Grid1.RowCount:=5000;
  Grid1.ShowScrollBars:=True;
end;

procedure TForm1.Grid1GetValue(Sender: TObject; const Col, Row: Integer;
  var Value: Variant);
begin
  if Col=0 then
    Value:='Row '+IntToStr(Row);

  if Col=1 then
    Value := 'Row '+IntToStr(Row);

// Apply style based on value ?

end;

end.

Many thanks in advance, Ian.

Community
  • 1
  • 1
Ian
  • 147
  • 1
  • 3
  • 8
  • Can you define 'based on a value'? Do you mean, say, if the value is negative then the font will be red etc? – Mike Sutton Feb 13 '12 at 17:01
  • Hi Mike - Yes; spot on. I have two scenarios but both are the same principle. One scenario is negative value is to be displayed RED and the other scenario is to "bold" an item in the list (that I choose - due to off grid held details; important customer etc...). Thanks in advance. Ian. – Ian Feb 13 '12 at 18:00

2 Answers2

5

Firstly, an apology. In my answer to your last question, CreateCellControl should have called inherited to create the cell. I've amended my answer.

As for this question, I've uploaded my blog posting on FireMonkey Cells - http://monkeystyler.com/blog/entry/firemonkey-grid-basics-custom-cells-and-columns - it covers the stuff from the previous answer, and also covers creating custom cell controls. You'll need to read that before your proceed. I'll wait.

...

Back now? Good.

Following on from the example in the blog post.

Except, that I've updated the TFinancialCell to inherit directly from TTextCell (which of course is a TEdit), which makes far more sense and is far simpler to style.

So, update the TFinancialCell:

type TFinancialCell = class(TTextCell)
  private
    FIsNegative: Boolean;
    FIsImportant: Boolean;
  protected
    procedure SetData(const Value: Variant); override;
    procedure ApplyStyle;override;
    procedure ApplyStyling;
  public
    constructor Create(AOwner: TComponent); override;
  published
    property IsNegative: Boolean read FIsNegative;
    property IsImportant: Boolean read FIsImportant;
  end;

Code for the above:

procedure TFinancialCell.ApplyStyle;
var T: TFMXObject;
begin
  inherited;
  ApplyStyling;
end;

procedure TFinancialCell.ApplyStyling;
begin
  if IsNegative then
    FontFill.Color := claRed
  else
    FontFill.Color := claBlack;
  Font.Style := [TFontStyle.fsItalic];
  if IsImportant then
    Font.Style := [TFontStyle.fsBold]
  else
    Font.Style := [];
  if Assigned(Font.OnChanged) then
    Font.OnChanged(Font);
  Repaint;
end;

constructor TFinancialCell.Create(AOwner: TComponent);
begin
  inherited;
  TextAlign := TTextAlign.taTrailing;
end;

procedure TFinancialCell.SetData(const Value: Variant);
var F: Single;
  O: TFMXObject;
  S: String;
begin
  S := Value;
  FIsImportant := S[1] = '#';
  if IsImportant then
    S := Copy(Value,2,MaxInt)
  else
    S := Value;

  F := StrToFloat(S);
  inherited SetData(Format('%m', [F]));
  FIsNegative := F < 0;
  ApplyStyling;
end;

And finally, update the GetValue event handler:

procedure TForm1.Grid1GetValue(Sender: TObject; const Col, Row: Integer;
  var Value: Variant);
var Cell: TStyledControl;
begin
  if Col = 0 then
    Value := Row
  else if Col = 1 then
  begin
    Value := FloatToStr(Data[Row]);
    if Value > 30 then
      Value := '#'+Value;
  end;
end;
Mike Sutton
  • 4,191
  • 4
  • 29
  • 42
  • Hi Mike - thanks again for the example; you are indeed the "TGrid guru". The blog post/board post are excellent and very well presented. I have managed to follow your revised/edited example and get the bold and red working well. - Many thanks again; a true gent...! Ian. (PS. Is there a way to right-align the column heading as well using the same methodology for the TFinancial columns..?) – Ian Feb 14 '12 at 00:57
  • Hi Mike - a slight issue I noted in relation to the above? All looks fine until I scroll the window. When it scrolls, the red/bolds go out of line/sync. I am not sure of it is a repaint issue with FM or something else? I can post my code if yours is fine? - Thx in advance, Ian. – Ian Feb 14 '12 at 01:32
  • My bad. Styling needs to be reapplied from the SetData method, so I have updated the code above to extract an ApplyStyles method and call it from ApplyStyle, SetData and SetIsImportant. – Mike Sutton Feb 14 '12 at 09:26
  • As for headers, it should be easy. In your columns constructor add if (Grid <> nil) and (Grid.FHeader <> nil) then Grid.FHeader.Items[Index].TextAlign := TTextAlign.alTrailing; - But Fheader is a private member, so you'll have to subclass TGrid to expose it. (And the TColumn doesn't expose it either - it's Header property is simply a string, not a THeader). – Mike Sutton Feb 14 '12 at 09:39
  • Thanks for the edit; no problem. I still seem to get a strange issue when I scroll however in that some of the values are bolded occasionally as you scroll, then if you scroll back up - they are not? I also had to change the "ApplyStyling" to "TFinancialCell.ApplyStyling"; was that correct - or did I cause the issue? - Thx Ian. – Ian Feb 14 '12 at 11:04
  • Hi Mike - I may have spotted what is causing the redraw issue for me in my example (could be a FM bug?). If you mouse scroll (wheel) the grid content; then it works as you designed. If you drag the scroll bar - it doesn't? - Not sure if this helps? – Ian Feb 14 '12 at 11:13
  • Oops. I didn't notice that one. So, the CellFromIndex stuff doesn't work at that point. The next option (as per updated code above) is to pass Value as a string, and prepend a # char to indicate a bolded item. Another alternative would be to add an event to your cell class, which would have to be set by the CreateCellControl of your column (which of course needs an added event property). Call the event from the SetData method and it can set any properties of the cell which you want changing. Or pass an object as the result of GetValue and extract it in SetData. – Mike Sutton Feb 14 '12 at 12:03
  • Worked - Thanks for persevering Mike. The TGrid is a fast control; but definately a FM v1.0 control. - Superstar...! – Ian Feb 14 '12 at 13:52
1

Code above is fine for versions before XE4, but for XE4 and XE5 does not work. Color and style of text is not changed.

This is a fixed code for XE4 and XE5:

procedure TFinancialCell.ApplyStyling;
begin
  StyledSettings := [TStyledSetting.ssFamily, TStyledSetting.ssSize];
  if IsNegative then
    FontColor := claRed
  else
    FontColor := claBlack;
  Font.Style := [TFontStyle.fsItalic];
  if IsImportant then
    Font.Style := [TFontStyle.fsBold]
  else
    Font.Style := [];
  if Assigned(Font.OnChanged) then
    Font.OnChanged(Font);
  Repaint;
end;
Alex Egorov
  • 907
  • 7
  • 26