[FMX] Grilles et couleurs
par
, 01/08/2022 à 10h46 (1096 Affichages)
Trois évènements sont proposés pour dessiner au sein d'une grille, et l'ordre d'exécution en est important.
Il m'a fallu, dans un premier temps, à comprendre pourquoi l'évènement onDrawColumnBackground ne se déclenchait pas dans mes programmes. Première ambiguïté, le nom de l'évènement fait croire que l'on va travailler sur la colonne entière, la documentation, indique bien qu'il s'agira de la zone d'une cellule, par contre, ce qui n'est pas indiqué, c'est que l'option AlternatingRowBackground doit être activée pour que l'événement soit levé.
Un simple programme
- Une grille contenant une seule colonne et une seule ligne ;
- Un mémo pour récupérer des informations ;
- Et le codage des 3 évènements.
m'a permis de trouver la hiérarchie de ceux-ci.
J'aurais pu en rester là, mais rien ne vaut une petite démo et de fil en aiguille, je me suis un peu pris au jeu.
Voici ce que donne une grille simple avec l'option AlternatingRowBackground active, dans ce cas, c'est le style qui fourni les couleurs de fond.
Il serait bien évidemment possible de modifier le style (au design ou au runtime) pour changer les deux couleurs mais, cela changerait toutes les grilles de la forme.
Coder l'évènement onDrawColumnBackground est plus simple.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 procedure TForm1.DrawStripesBackground(Sender: TObject; const Canvas: TCanvas; const Column: TColumn; const Bounds: TRectF; const Row: Integer; const Value: TValue; const State: TGridDrawStates); var aBrush : TBrush; // begin aBrush:=TBrush.Create(TBrushKind.Solid,TAlphaColors.Null); try if odd(row) then aBrush.Color:=TAlphaColors.white else aBrush.Color:=TAlphaColors.red; Canvas.FillRect(Bounds,1,aBrush); finally aBrush.Free; end; end;
Cela ne vous rappelle pas quelque chose ? Oui, c'est à dessein que j'ai utilisé l'alternance de blanc et rouge et cela va montrer l'utilisation de OnDrawColumnCell.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56 procedure TForm3.btnUSAClick(Sender: TObject); var I: Integer; const oddstars ='* * * * * *'; evenstars=' * * * * * *'; begin StringGrid1.OnDrawColumnBackground:=DrawStripesBackground; StringGrid1.OnDrawColumnCell:=DrawStarsColumnCell; Stringgrid1.ClearColumns; StringGrid1.RowCount:=13; StringGrid1.RowHeight:=20; with TColumn.Create(StringGrid1) do parent:=StringGrid1; with TColumn.Create(StringGrid1) do parent:=StringGrid1; with TColumn.Create(StringGrid1) do parent:=StringGrid1; for I := 0 to 7 do begin if odd(i) then StringGrid1.Cells[0,i]:=oddstars else StringGrid1.Cells[0,i]:=evenstars; end; end; procedure TForm1.testDrawStarsColumnCell(Sender: TObject; const Canvas: TCanvas; const Column: TColumn; const Bounds: TRectF; const Row: Integer; const Value: TValue; const State: TGridDrawStates); var aTextLayout : TTextLayout; aRectF : TrectF; aBrush : TBrush; begin if Value.IsEmpty then exit; aBrush:=TBrush.Create(TBrushKind.Solid,TAlphaColors.Null); try if (column.Index=0) AND (row<=7) then aBrush.Color:=TAlphaColors.Midnightblue; aRectF:=Bounds; aRectf.Inflate(3,3); // agrandir le rectangle Canvas.FillRect(aRectF,1,aBrush); // utilisation d'un TextLayout plutôt qu'un simple Canvas.Drawtext // permet de changer la couleur, la taille de la fonte, l'alignement etc... ATextLayout:=TTextLayoutManager.TextLayoutByCanvas(Canvas.ClassType).Create(Canvas); ATextLayout.BeginUpdate; ATextLayout.Text:=Value.ToString; ATextLayout.Color:=Talphacolors.Antiquewhite; ATextLayout.TopLeft:=Bounds.TopLeft; ATextLayout.MaxSize := PointF(ArectF.Width, aRectf.Height); ATextLayout.Font.Size:=20; ATextLayout.VerticalAlign:=TTextAlign.Center; ATextLayout.HorizontalAlign:=TTextAlign.Center; ATextLayout.EndUpdate; ATextLayout.RenderLayout(Canvas); finally ATextLayout.Free; aBrush.Free; end; end;
Ce que j'ai découvert, à ce stade, c'est que la taille des rectangles (Bounds) est différente entre les deux évènements, d'où la nécessité d'agrandir le rectangle ligne 35 du code.
NB. j'ai caché les entêtes, option Header ôtée, mais gardé les options ColLines et RowLines pour bien montrer qu'il s'agit d'une grille.
J'aurais pu m'arrêter là mais, poussé par la curiosité, je me suis lancé dans la construction d'un échiquier
Le challenge pour ce dernier était plus le dessin des pièces
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62 procedure TForm1.btnPionsClick(Sender: TObject); var I: Integer; c : string; // couleur du pion begin if Tbutton(Sender).Tag=0 then c:='b' else c:='w'; // choix blancs ou noirs StringGrid1.OnDrawColumnBackground:=StringGrid1DrawChessBackground; StringGrid1.OnDrawColumnCell:=StringGrid1DrawPawnColumnCell; Stringgrid1.ClearColumns; StringGrid1.RowCount:=8; StringGrid1.RowHeight:=40; stringGrid1.Height:=347; stringGrid1.width:=347; for I := 1 to 8 do begin with TColumn.Create(StringGrid1) do begin parent:=StringGrid1; width:=40; end; end; for I := 0 to 7 do StringGrid1.Cells[i,1]:='P'+c; StringGrid1.Cells[0,0]:='T'+c; StringGrid1.Cells[7,0]:='T'+c; StringGrid1.Cells[1,0]:='C'+c; StringGrid1.Cells[6,0]:='C'+c; StringGrid1.Cells[2,0]:='B'+c; StringGrid1.Cells[5,0]:='B'+c; StringGrid1.Cells[3,0]:='Q'+c; StringGrid1.Cells[4,0]:='K'+c; if c='w' then c:='b' else c:='w'; for I := 0 to 7 do StringGrid1.Cells[i,6]:='P'+c; StringGrid1.Cells[0,7]:='T'+c; StringGrid1.Cells[7,7]:='T'+c; StringGrid1.Cells[1,7]:='C'+c; StringGrid1.Cells[6,7]:='C'+c; StringGrid1.Cells[2,7]:='B'+c; StringGrid1.Cells[5,7]:='B'+c; StringGrid1.Cells[3,7]:='Q'+c; StringGrid1.Cells[4,7]:='K'+c; end; procedure TForm1.StringGrid1DrawChessBackground(Sender: TObject; const Canvas: TCanvas; const Column: TColumn; const Bounds: TRectF; const Row: Integer; const Value: TValue; const State: TGridDrawStates); var aBrush : TBrush; begin aBrush:=TBrush.Create(TBrushKind.Solid,TAlphaColors.Null); try if (odd(column.Index) AND odd(row)) OR NOT(odd(column.Index) OR odd(row)) then aBrush.Color:=TAlphaColors.Ivory else aBrush.Color:=TAlphaColors.Chocolate; Canvas.FillRect(Bounds,1,aBrush); finally aBrush.Free; end; end;
Les entêtes étant vraiment séparées du reste, je n'ai pas vraiment cherché à tester l'événement associé OnDrawColumnHeader si ce n'est pour en trouver son niveau de hiérarchie. Il peut quand même être intéressant de noter que cet évènement se déclenche une fois toutes les cellules prises en compte.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52 procedure TForm3.StringGrid1DrawColumnCell(Sender: TObject; const Canvas: TCanvas; const Column: TColumn; const Bounds: TRectF; const Row: Integer; const Value: TValue; const State: TGridDrawStates); const bishop='M19,22H5V20H19V22M17.16,8.26C18.22,9.63 18.86,11.28 19,13C19,15.76 15.87,18 12,18'+ 'C8.13,18 5,15.76 5,13C5,10.62 7.33,6.39 10.46,5.27C10.16,4.91 10,4.46 10,4A2,2 0 0,1'+ ' 12,2A2,2 0 0,1 14,4C14,4.46 13.84,4.91 13.54,5.27C14.4,5.6 15.18,6.1 15.84,6.74L11.29,11.29L12.71,12.71L17.16,8.26Z'; king='M19,22H5V20H19V22M17,10C15.58,10 14.26,10.77 13.55,12H13V7H16V5H13V2H11V5H8V7H11V12H10.45C9.35,10.09 6.9,9.43 5,10.54C3.07,11.64 2.42,14.09 3.5,16C4.24,17.24 5.57,18 7,18H17A4,4 0 0,0 21,14A4,4 0 0,0 17,10Z'; cavalier='M19,22H5V20H19V22M13,2V2C11.75,2 10.58,2.62 9.89,3.66L7,8L9,10L11.06,8.63C11.5,8.32 '+ '12.14,8.44 12.45,8.9C12.47,8.93 12.5,8.96 12.5,9V9C12.8,9.59 12.69,10.3 12.22,10.77L7.42,15.57C6.87,16.13 6.87,17.03 7.43,17.58C7.69,17.84 8.05,18 8.42,18H17V6A4,4 0 0,0 13,2Z'; pion='M19 22H5V20H19V22M16 18H8L10.18 10H8V8H10.72L10.79 7.74C10.1 7.44 9.55 6.89 9.25 6.2C8.58 4.68 9.27 2.91 10.79 2.25C12.31 1.58 14.08 2.27 14.74 3.79C15.41 5.31 14.72 7.07 13.2 7.74L13.27 8H16V10H13.82L16 18Z'; queen='M18,3A2,2 0 0,1 20,5C20,5.81 19.5,6.5 18.83,6.82L17,13.15V18H7V13.15L5.17,6.82'+ 'C4.5,6.5 4,5.81 4,5A2,2 0 0,1 6,3A2,2 0 0,1 8,5C8,5.5 7.82,5.95 7.5,6.3L10.3,9.35L10.83,5.62C10.33,5.26 10,4.67 10,4A2,2 0 0,1 12,2A2,2 0 0,1 14,4C14,4.67 13.67,5.26 13.17,5.62L13.7,9.35L16.47,6.29C16.18,5.94 16,5.5 16,5A2,2 0 0,1 18,3M5,20H19V22H5V20Z'; tower='M5,20H19V22H5V20M17,2V5H15V2H13V5H11V2H9V5H7V2H5V8H7V18H17V8H19V2H17Z'; var aBrush : TBrush; aRectf : TRectF; aPath : TPath; begin if Value.IsEmpty then exit; aBrush:=TBrush.Create(TBrushKind.Solid,TAlphaColors.Null); try // effacer le texte existant aRectF:=Bounds; aRectf.Inflate(3,3); aRectF.Left:=aRectf.Left-3; if (odd(column.Index) AND odd(row)) OR NOT(odd(column.Index) OR odd(row)) then aBrush.Color:=TAlphaColors.Ivory else aBrush.Color:=TAlphaColors.Chocolate; Canvas.FillRect(arectf,1,aBrush); if value.ToString.EndsWith('w') then ABrush.color:=Talphacolors.Aqua else ABrush.color:=Talphacolors.black; // remplacement du texte par un dessin SVG aPath:=Tpath.Create(nil); if value.ToString.StartsWith('K') then aPath.Data.Data:=King; if value.ToString.StartsWith('Q') then aPath.Data.Data:=Queen; if value.ToString.StartsWith('B') then aPath.Data.Data:=bishop; if value.ToString.StartsWith('C') then aPath.Data.Data:=cavalier; if value.ToString.StartsWith('T') then aPath.Data.Data:=Tower; if value.ToString.StartsWith('P') then aPath.Data.Data:=Pion; aPath.Fill:=aBrush; aPath.width:=40; aPath.Height:=40; aPath.Parent:=Self; aPath.PaintTo(Canvas,Bounds,Stringgrid1); // dessin dans la grille finally aPath.free; aBrush.free; end; end;