IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Voir le flux RSS

Blog de Serge Girard (aka SergioMaster)

[FMX] Grilles et couleurs

Noter ce billet
par , 01/08/2022 à 09h46 (738 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.

Nom : Capture_1.PNG
Affichages : 24
Taille : 44,0 Ko

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.

Nom : Capture_1.PNG
Affichages : 23
Taille : 3,6 Ko

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;
Nom : Capture_2.PNG
Affichages : 21
Taille : 3,7 Ko

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;
Nom : Capture_3.PNG
Affichages : 21
Taille : 6,6 Ko

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

Nom : Capture_4.PNG
Affichages : 20
Taille : 21,7 Ko

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;
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
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;
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.

Envoyer le billet « [FMX] Grilles et couleurs » dans le blog Viadeo Envoyer le billet « [FMX] Grilles et couleurs » dans le blog Twitter Envoyer le billet « [FMX] Grilles et couleurs » dans le blog Google Envoyer le billet « [FMX] Grilles et couleurs » dans le blog Facebook Envoyer le billet « [FMX] Grilles et couleurs » dans le blog Digg Envoyer le billet « [FMX] Grilles et couleurs » dans le blog Delicious Envoyer le billet « [FMX] Grilles et couleurs » dans le blog MySpace Envoyer le billet « [FMX] Grilles et couleurs » dans le blog Yahoo

Commentaires

  1. Avatar de NABIL74
    • |
    • permalink
    Bonjour Sergio,

    Merci beaucoup pour ces astuces très pratiques !! Toujours des articles TOP!