Bonjour,
Peut on fusionner 2 cellules comme dans Excel dans un StringGrid
Bonjour,
Peut on fusionner 2 cellules comme dans Excel dans un StringGrid
non pas avec TStringGrid
Peut-être d'autres composants spéc. le font, j'en connais pas.
Il existe deux composants qui le font, descendants de TStringGrid et de TDrawGrid.
Ceux ci, écrits pour Delphi 5, ne devraient pas être trop difficiles à installer pour des versions ultérieures de Delphi.En plus ils sont freeware et fournis avec le code source.
Tu peux les trouver ici :
http://www.programmers.net/mirrors/D...0/f010_001.htm
... à la rubrique EGrid.zip
ou bien les télécharger directement là :
http://www.programmers.net/mirrors/D...free/EGrid.zip
Bon dev
J'ai trouvé mieux : le faire soi même n'est pas si compliqué
Je sens que je vais le rajouter dans la FAQ...
Voici la fonction qui permet de le faire :
Utilisation de la fonction MergedCells(AStringGrid:TStringGrid;CurrentCol,CurrentRow,Col1,Row1,Col2,Row2:Integer;CurrentState: TGridDrawState):Boolean; :
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 function MergedCells(AStringGrid:TStringGrid;CurrentCol,CurrentRow,Col1,Row1,Col2,Row2:Integer;CurrentState: TGridDrawState):Boolean; var i,j:Integer; x1,y1,x2,y2:Integer; ARect:TRect; begin //Initialisations diverses ARect:=Bounds(0,0,0,0); x1:=Col1; y1:=Row1; x2:=Col2; y2:=Row2; result:=False; //On vérifie aue la zone fusionnée est valide if x1<0 then x1:=0; if x2>AStringGrid.ColCount-1 then x2:=AStringGrid.ColCount-1; if y1<0 then y1:=0; if y2>AStringGrid.RowCount-1 then x2:=AStringGrid.RowCount-1; if (x1>x2) or (y1>y2) then begin result:=False; Exit; end; //Si la cellule courante est la dernière de la zone de fusion, on dessine dans la fusion le texte de la cellule en haut à gauche if ((CurrentCol=Col2) and (CurrentRow=Row2)) then begin ARect.Left:=AStringGrid.CellRect(Col1,Row1).Left; ARect.Top:=AStringGrid.CellRect(Col1,Row1).Top; ARect.Right:=AStringGrid.CellRect(Col2,Row2).Right; ARect.bottom:=AStringGrid.CellRect(Col2,Row2).Bottom; AStringGrid.Canvas.TextRect(ARect, ARect.Left+2, ARect.Top+2, AStringGrid.Cells[Col1,Row1]); result:=True; end; //Si la cellule courante est dans la zone de fusion, on dit qu'on la dessiné (même si ce n'est pas vrai :) ) if ((CurrentCol>=Col1) and (CurrentRow>=Row1) and (CurrentCol<=Col2) and (CurrentRow<=Row2)) then result:=True; end;
- Elle s'utilise dans la méthode
de l'évènement StringGrid11.OnDrawCell
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState);- AStringGrid est le StringGrid concerné par la fusion de celluele, (StringGrid1 dans l'exemple qui suit...)
- CurrentCol,CurrentRow,CurrentState sont les paramètres ACol,Arow et State fournis par l'évènement OnDrawCell
- Col1,Row1 sont les coordonnées (Colonne, Ligne) de la cellule supérieure gauche
- Col2,Row2 sont les coordonnées (Colonne, Ligne) de la cellule inférieure droite
- Valeur renvoyée : True si la cellule à été prise en charge par la fusion (autrement dit si elle se trouve dans la zone définie par Col1,Row1, Col2 et Row2), sinon False
Exemple :
Dans un nouveau projet, sur ma fiche, j'ai juste mis un TStringGrid
et ce code :
Ne pas oublier de lier :
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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87 unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, Grids; type TForm1 = class(TForm) StringGrid1: TStringGrid; procedure StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState); procedure FormCreate(Sender: TObject); private { Déclarations privées } public { Déclarations publiques } end; var Form1: TForm1; implementation {$R *.dfm} function MergedCells(AStringGrid:TStringGrid;CurrentCol,CurrentRow,Col1,Row1,Col2,Row2:Integer;CurrentState: TGridDrawState):Boolean; var i,j:Integer; x1,y1,x2,y2:Integer; ARect:TRect; begin //Initialisations diverses ARect:=Bounds(0,0,0,0); x1:=Col1; y1:=Row1; x2:=Col2; y2:=Row2; result:=False; //On vérifie aue la zone fusionnée est valide if x1<0 then x1:=0; if x2>AStringGrid.ColCount-1 then x2:=AStringGrid.ColCount-1; if y1<0 then y1:=0; if y2>AStringGrid.RowCount-1 then y2:=AStringGrid.RowCount-1; if (x1>x2) or (y1>y2) then begin result:=False; Exit; end; //Si la cellule courante est la dernière de la zone de fusion, on dessine dans la fusion le texte de la cellule en haut à gauche if ((CurrentCol=Col2) and (CurrentRow=Row2)) then begin ARect.Left:=AStringGrid.CellRect(Col1,Row1).Left; ARect.Top:=AStringGrid.CellRect(Col1,Row1).Top; ARect.Right:=AStringGrid.CellRect(Col2,Row2).Right; ARect.bottom:=AStringGrid.CellRect(Col2,Row2).Bottom; AStringGrid.Canvas.TextRect(ARect, ARect.Left+2, ARect.Top+2, AStringGrid.Cells[Col1,Row1]); result:=True; end; //Si la cellule courante est dans la zone de fusion, on dit qu'on la dessiné (même si ce n'est pas vrai :) ) if ((CurrentCol>=Col1) and (CurrentRow>=Row1) and (CurrentCol<=Col2) and (CurrentRow<=Row2)) then result:=True; end; procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState); begin //On dessine la ou les cellules //d'abord on teste et s'occupe des fusions de cellule if not MergedCells(StringGrid1,ACol,ARow,2,2,2,3,State)then if not MergedCells(StringGrid1,ACol,ARow,3,3,4,3,State)then //puis si on a pas une cellule fusionnée on la dessine normalement StringGrid1.Canvas.TextRect(Rect, Rect.Left+2, Rect.Top+2, StringGrid1.Cells[ACol,ARow]); end; procedure TForm1.FormCreate(Sender: TObject); begin Stringgrid1.Cells[1,1]:='coucou'; Stringgrid1.Cells[2,2]:='Salut'; Stringgrid1.Cells[3,3]:='Bonjour à tout le monde'; end; end.
à l'évènement Form1.OnCreate
Code : Sélectionner tout - Visualiser dans une fenêtre à part procedure TForm1.FormCreate(Sender: TObject);
Ainsi que :
à l'évènement StringGrid11.OnDrawCell
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3 procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState);
[Edit]Code mis à jour
Ca marche, meme si je n'ai pas tout compris au code ... sauf qu'il faut changer dans la procedure TForm1.StringGrid1DrawCell pour selectionner les cellules a fusionner
Merci
Toutes les explications seront dans la FAQ
en attendant, si celà répond à ton besoin, n'oublie pas le petit bouton "résolu" en bas à gauche de cette page...
Encore deux questions :
Lors d'une fusion horizontale ou verticale, peux-tu centrer le texte ?
Quand la FAQ sera prete ? je suis impatient de comprendre ...
Encore une question + 1 remarque :
- 2 cellules fusionnées, ok. Mais 3, comment faire ?
- Tu utilises :
i et j ne sont jamais utilisées ... C'est ma modeste participation
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 function MergedCells (AStringGrid:TStringGrid;CurrentCol,CurrentRow,Col1,Row1,Col2,Row2:Integer;CurrentState: TGridDrawState):Boolean; var i,j:Integer;
Tiens voilà le principe de fonctionnement qui sera inclu dans la FAQ :
J'espère que ceci t'éclairera sur le fonctionnement de ma fonction.L'idée est de pouvoir tirer partie du fait que l'on puisse dessiner n'importe où sur le Canvas d'un StringGrid dans l'évènement OnDrawCell de ce dernier.
OnDrawCell ne fait rien d'autre que de nous fournir les coordonnées (ACol, ARow) de la cellule qui est prète à être dessinée, son rectangle de délimitation (Rect) et son Etat (State). Rien ne nous empêche de dessiner ce que l'on veut en dehors de la zone Rect qui nous est proposée...
Imaginons que nous souhaitons fusionner les cellules où
1<=Colonne<=3
2<=Ligne<=3
Nous dessinerons alors le texte de la cellule située ayant pour cordonnées (Colonne,Ligne)=(1,2) sur le rectangle s'étendant du coin supérieur gauche de la cellule (1,2) au coin inférieur droit de la cellule (3,3)
Le seul hic, c'est que si nous dessinons le texte de la cellule (1,2) dans cette zone, au moment où OnDrawCell[/b est déclanché avec (ACol,Arow)=(1,2) dans cette nouvelle zone de dessin que nous avons choisie, Delphi re-préparera le Dessin de fond des cellules (2,2), (2,3) etc...
Donc notre texte semblera coupé dans l'espace de la cellule supérieure gauche.
Le Dessin de la cellule fusionnée doit donc se faire lorsque Delphi déclenche le OnDrawCell avec (ACol,Arow)=(3,3) (c'est-à-dire la cellule en bas à droite de notre zone fusionnée)
Voici une fonction, destinée à être appelée dans l'évènement OnDrawCell et qui utilise ce principe :
Sinon, pour centrer le texte dans la cellule, il faut que tu joues avec les
Canvas.TextRect();
Tu en as 2, 1 dans la fonction et 1 dans StringGrid1DrawCell :
et
Code : Sélectionner tout - Visualiser dans une fenêtre à part AStringGrid.Canvas.TextRect(ARect, ARect.Left+2, ARect.Top+2, AStringGrid.Cells[Col1,Row1]);
C'est dans ces lignes que le texte est positionné...
Code : Sélectionner tout - Visualiser dans une fenêtre à part StringGrid1.Canvas.TextRect(Rect, Rect.Left+2, Rect.Top+2, StringGrid1.Cells[ACol,ARow]);
Maintenant, jette un oeil sur ce que dit l'aide Delphi :
Donc si tu veux centrer ton texte, tu ajouteras AlignCenter en écrivant :procedureTextRect(Rect:TRect;X,Y:Integer;constText:WideString;TextFlags:Integer =0);
Syntaxe C++:
void __ fastcallTextRect(constTRect&Rect,intX,intY,constWideStringText,intTextFlags =0);
Description
Utilisez la méthode TextRect pour écrire une chaîne àl'intérieur des limites d'un rectangle.Toute partie de la chaîne sortant du rectangle défini par le paramètre Rect est rognée et n'apparaît pas.Le coin supérieur gauche du texte est placéau point (X,Y).
Le paramètre TextFlags spécifie comment le texte doit être alignédans le rectangle.Il s'agit d'une valeur entière représentant la somme d'un identificateur d'alignement horizontal et d'un identificateur d'alignement vertical.Les identificateurs d'alignement sont les suivants :
Indicateur Type Signification
AlignLeft Horizontal Aligner le texte sur le côtégauche du rectangle.
AlignRight Horizontal Aligner le texte sur le côtédroit du rectangle.
AlignHCenter Horizontal Centrer le texte horizontalement dans le rectangle.
AlignTop Vertical Aligner le texte sur le bord supérieur du rectangle.
AlignBottom Vertical Aligner le texte sur le bord inférieur du rectangle.
AlignVCenter Vertical Centrer le texte verticalement dans le rectangle.
En outre,vous pouvez utiliser l'identificateur AlignCenter pour indiquer AlignHCenter +AlignVCenter.
et
Code : Sélectionner tout - Visualiser dans une fenêtre à part AStringGrid.Canvas.TextRect(ARect, ARect.Left+2, ARect.Top+2, AStringGrid.Cells[Col1,Row1],AlignCenter);
De plus, de nombreuses FAQ t'aiderons à parfaire le "look" de ton stringgrid (couleurs, multiligne,...)
Code : Sélectionner tout - Visualiser dans une fenêtre à part StringGrid1.Canvas.TextRect(Rect, Rect.Left+2, Rect.Top+2, StringGrid1.Cells[ACol,ARow],AlignCenter);
http://delphi.developpez.com/faq/?page=stringgrid
Tu as raison i et j sont en tropEnvoyé par yanba
Sinon, comme je l'ai dit (Col1,Row1) est la cellule supérieure gauche, (Col2,Row2), celle qui est en bas à droite de ta zone fusionnée, donc tu peux fusionner autant de cellule que tu veux !!!
exemple essayes ce rectangle de 4x5 cellules :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9 procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState); begin //On dessine la ou les cellules //d'abord on teste et s'occupe des fusions de cellule if not MergedCells(StringGrid1,ACol,ARow,1,1,3,4,State)then //puis si on a pas une cellule fusionnée on la dessine normalement StringGrid1.Canvas.TextRect(Rect, Rect.Left+2, Rect.Top+2, StringGrid1.Cells[ACol,ARow]); end;
1 - Pour la fusion de plusieurs cellulles, c'était trop evident !!! Je cherchais midi à quatorze heures ...
2 - Pour centrer, j'ai bien modifier, mais j'ai le message suivant : trop de paramètres originaux.
3 - Je commence à comprendre ton explication mais comment se fait-il que F1 Delphi ne connaisse pas MergedCells ? Je voulais savoir quand utilise ton cette instruction ?
Envoyé par yanba
- Ha bah oui...
- je t'ai indiqué les paramètres TextRect sous CLX ! Je suis désolé (décidemment je suis impardonnable) . Comme quoi tout le monde peux faire des erreurs ! voir ci-dessous...
- Delphi F1 ne connait pas MergedCells tout simplement parce que c'est ma propre réalisation (Copyright Waskol : Open Source libre de droits).
Là on l'occurence comme je l'ai dit dans mes messages précédents, il faut utiliser ma fonction dans l'évènement du OnDrawCell.
Donc, pour centrer du texte, aussi bien que pour écrire du multiligne dans les cellules, il faut utiliser la fonction de l'API Windows DrawText
Ca donne en fait des explications beaucoup plus compliquées que j'éviterais.
Celà dit j'ai les quelques fonctions qui le permettent sous le coude, prêtes a être postées sur le forum
Si je le fait, je ne dirais pas comment ça marche (trop long à mettre sur le forum) --> Ouah l'excuse l'autre
Par contre je veux bien dire comment on l'utilise
J'envoie le paquet ? Attention aux yeux, ça fait peur...
- Delphi F1 ne connait pas MergedCells tout simplement parce que c'est ma propre réalisation (Copyright Waskol : Open Source libre de droits).
Excuse moi, j'avais cliqué trop vite, je m'en suis rendu compte de mon absurdité lorsque j'ai relu ce message ce matin.
- Envoie ton paquet, Moi rien me fait peur !
- Sinon, je pense avoir bien compris ton code meme si je serais incapable de le refaire !
- Je voudrais te proposer une modification de ma part qui ne marche pas C'est la date 09/02/06 qui ne s'affiche pas) :
J'ai 20 colonnes ...
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 procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;Rect: TRect; State: TGridDrawState); begin //d'abord on teste et on s'occupe des fusions de cellule Nbre:=5; while Nbre<stringgrid1.ColCount do begin if not MergedCells(StringGrid1,ACol,ARow,1,1,Nbre,1,State) then //puis si on a pas une cellule fusionnée on la dessine normalement StringGrid1.Canvas.TextRect(Rect, Rect.Left+2, Rect.Top+2, StringGrid1.Cells[ACol,ARow]); Nbre:=Nbre+5; end; end; procedure TForm1.FormCreate(Sender: TObject); var LaDate:TdateTime; begin Nbre:=0; LaDate:=EncodeDate(2006,02,07); while Nbre<stringgrid1.ColCount do begin LaDate:=LaDate+1; Stringgrid1.Cells[1+Nbre,1]:=DateToStr(LaDate); Stringgrid1.Cells[1+Nbre,2]:='Matin'; Stringgrid1.Cells[2+Nbre,2]:='Midi'; Stringgrid1.Cells[3+Nbre,2]:='16h'; Stringgrid1.Cells[4+Nbre,2]:='Soir'; Stringgrid1.Cells[5+Nbre,2]:='Nuit'; Nbre:=Nbre+5; end; end;
Tu l'auras voulu...
D'abord une (petite) unité :
[Mis à Jour le 4/3/2006]
J'avais prévenu
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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292 unit UnitFusionStringGrid; interface uses Windows, Classes, Graphics, StdCtrls,SysUtils,Grids; type TAlignementVertical=(alVTop,alVCenter,alVBottom); TAlignementHorizontal=(alHLeft,alHCenter,alHRight); TJustification=(JustLeft,JustCenter,JustRight); TZoneFusionnee=record ColLeft,RowTop,ColRight,RowBottom:Integer; AlignementHorizontal:TAlignementHorizontal; AlignementVertical:TAlignementVertical; TextJustification:TJustification; State:TGridDrawState; ZoneValide:Boolean; end; TParametresOnDrawCell=record LeStringGrid:TStringGrid; ACol,ARow:Integer; Rect:TRect; State:TGridDrawState; end; //utilitaire pour la visibilité du code function SetZoneFusionnee(AStringGrid:TStringGrid;ZoneText:string; ColLeft,RowTop,ColRight,RowBottom:Integer; AlignementHorizontal:TAlignementHorizontal; AlignementVertical:TAlignementVertical; TextJustification:TJustification):TZoneFusionnee; //Pour dessiner une cellule simple (utilisée par MergedCells une zone) procedure DessineCellule(ParametresOnDrawCell:TParametresOnDrawCell; AlignementHorizontal:TAlignementHorizontal; AlignementVertical:TAlignementVertical; TextJustification:TJustification); //Pour dessiner une fusion de cellules function DessineZone(ParametresOnDrawCell:TParametresOnDrawCell;var ZoneFusionnee:TZoneFusionnee):Boolean; //pour vérifier si une cellule se trouve dans la zone fusionnee spécifiee; function CelluleEstDansZone(ACol,Arow:Integer;ZoneFusionnee:TZoneFusionnee):Boolean; implementation uses Types; function CelluleEstDansZone(ACol,Arow:Integer;ZoneFusionnee:TZoneFusionnee):Boolean; begin with ZoneFusionnee do result:=((ACol>=ColLeft) and (ARow>=RowTop) and (ACol<=ColRight) and (ARow<=RowBottom)); end; function CelluleCouranteEstDansZone(ParametresOnDrawCell:TParametresOnDrawCell;ZoneFusionnee:TZoneFusionnee):Boolean; begin with ParametresOnDrawCell do result:=CelluleEstDansZone(ACol,ARow,ZoneFusionnee); end; function ZoneEstValide(AStringGrid:TStringGrid;var AZone:TZoneFusionnee):Boolean; begin with AZone do begin //on modifie la zone pour qu'elle rentre dans le stringgrid if ColLeft<0 then ColLeft:=0; if ColRight>AStringGrid.ColCount-1 then ColRight:=AStringGrid.ColCount-1; if RowTop<0 then RowTop:=0; if RowBottom>AStringGrid.RowCount-1 then RowBottom:=AStringGrid.RowCount-1; //On vérifie aue la zone fusionnée est valide ZoneValide:=(ColLeft<=ColRight) and (RowTop<=RowBottom); end; Result:=AZone.ZoneValide; end; function SetZoneFusionnee(AStringGrid:TStringGrid;ZoneText:string; ColLeft,RowTop,ColRight,RowBottom:Integer; AlignementHorizontal:TAlignementHorizontal; AlignementVertical:TAlignementVertical; TextJustification:TJustification):TZoneFusionnee; var AZone:TZoneFusionnee; begin AZone.ColLeft:=ColLeft; AZone.RowTop:=RowTop; AZone.ColRight:=ColRight; AZone.RowBottom:=RowBottom; AZone.AlignementHorizontal:=AlignementHorizontal; AZone.AlignementVertical:=AlignementVertical; AZone.TextJustification:=TextJustification; if ZoneEstValide(AStringGrid,AZone) then AStringGrid.Cells[ColLeft,RowTop]:=ZoneText; result:=AZone; end; //Voir FAQ Delphi de <a href="http://www.developpez.com" target="_blank">www.developpez.com</a> pour cette fonction : //URL : http://delphi.developpez.com/faq/?page=typechaine#dimensionstexte Function TextSize(Phrase : string; Police : TFont = nil) : TPoint; var DC: HDC; X: Integer; Rect: TRect; C : TBitmap; begin C := TBitmap.create; if police <> nil then C.canvas.Font := police; Rect.Left := 0; Rect.Top:=0; Rect.Right:=0; Rect.Bottom:=0; DC := GetDC(0); C.Canvas.Handle := DC; DrawText(C.Canvas.Handle, PChar(Phrase), -1, Rect, (DT_EXPANDTABS or DT_CALCRECT)); C.Canvas.Handle := 0; ReleaseDC(0, DC); result.X:=Rect.Right-Rect.Left; result.Y:=Rect.Bottom-Rect.Top; C.Free; end; procedure DessineTexteMultiligne(AString: string;ACanvas:TCanvas;ARect: TRect; AlignementHorizontal:TAlignementHorizontal; AlignementVertical:TAlignementVertical; TextJustification:TJustification); var AHeight,AWidth:integer; Rect,oldClipRect:TRect; ATop,ALeft,H,W:Integer; AText:string; JustificationDuTexte:Integer; MyRgn:HRGN; begin with ACanvas do begin Lock; AHeight:=ARect.Bottom-ARect.Top; AWidth:=ARect.Right-ARect.Left; //on calcule la taille du rectangle dans lequel va tenir le texte W:=TextSize(AString,ACanvas.Font).X; H:=TextSize(AString,ACanvas.Font).Y; //on calcule la position (Haut,Gauche) du rectangle dans lequel va tenir le texte //en fonction de l'alignement horizontal et vertical choisis ATop:=ARect.Top; ALeft:=ARect.Left; case AlignementVertical of alVBottom : ATop:=ARect.Bottom-H; alVCenter : ATop:=ARect.Top+((AHeight-H) div 2); alVTop : ATop:=ARect.Top; end; case AlignementHorizontal of alHLeft : ALeft:=ARect.Left; alHCenter: ALeft:=ARect.Left+(AWidth-W) div 2; alHRight : ALeft:=ARect.Right-W; end; //Fin du calcul du rectangle, on met le resultat dans Rect Rect:=Bounds(ALeft,ATop,W,H); //On rempli le rectangle de la zone sinon on voit le texte que delphi à dessiné FillRect(ARect); //On détermine les paramètres de justification à passer à Windows case TextJustification of JustLeft : JustificationDuTexte:=DT_LEFT; JustCenter: JustificationDuTexte:=DT_CENTER; JustRight : JustificationDuTexte:=DT_RIGHT; end; //Si le texte est plus grand que notre zone, on prend cette précaution (Clipping) with ARect do MyRgn :=CreateRectRgn(Left,Top,Right,Bottom); SelectClipRgn(Handle,MyRgn); //On dessine le texte DrawText(Handle,PChar(AString),-1,Rect,JustificationDuTexte or DT_NOPREFIX or DT_WORDBREAK ); //On a plus besoin de la zone de clipping SelectClipRgn(Handle,0); DeleteObject(MyRgn); Unlock; end; end; procedure DessineCellule(ParametresOnDrawCell:TParametresOnDrawCell; AlignementHorizontal:TAlignementHorizontal; AlignementVertical:TAlignementVertical; TextJustification:TJustification); var s:string; begin with ParametresOnDrawCell do begin s:=LeStringGrid.Cells[ACol,ARow]; DessineTexteMultiligne(s,LeStringGrid.Canvas,Rect,AlignementHorizontal,AlignementVertical,TextJustification); end; end; //Pour calculer la position de la cellule (LeftCol,TopRow) dans le StringGrid // --> voir CalcRect ci dessous function CalcRectOffset(ASG:TStringGrid):TPoint; var i,j:integer; x,y:Integer; begin with ASG do begin x:=0; y:=0; if LeftCol>FixedCols then for i:=FixedCols to LeftCol-1 do inc(x,ColWidths[i]+GridLineWidth); if TopRow>FixedRows then for j:=FixedRows to TopRow-1 do inc(y,Top+RowHeights[j]+GridLineWidth); end; Result:=Point(-x,-y); end; //Pour calculer le rectangle d'une cellule même s'il n'est pas visible (utilise CalcRectOffset) function CalcRect(ASG:TStringGrid;ACol,ARow:integer):TRect; var i,j:integer; ARect:TRect; Offsets:TPoint; begin Offsets:=CalcRectOffset(ASG); with ASG do begin ARect:=Bounds(Offsets.X,Offsets.Y,0,0); if ACol>0 then for i:=0 to ACol-1 do ARect.Left:=Arect.Left+ColWidths[i]+GridLineWidth; //ARect.Left:=ARect.Left+GridLineWidth; ARect.Right:=ARect.Left; ARect.Right:=ARect.Right+ColWidths[ACol]; if ARow>0 then for j:=0 to ARow-1 do ARect.Top:=Arect.Top+RowHeights[j]+GridLineWidth; //ARect.Top:=ARect.Top+GridLineWidth; ARect.Bottom:=ARect.Top; ARect.Bottom:=ARect.Bottom+RowHeights[ARow]; end; Result:=ARect; end; function DessineZone(ParametresOnDrawCell:TParametresOnDrawCell; var ZoneFusionnee:TZoneFusionnee):Boolean; var i,j:Integer; ARect:TRect; UneZone:TZoneFusionnee; DesParametres:TParametresOnDrawCell; begin //Initialisations diverses result:=False; ARect:=Bounds(0,0,0,0); //On vérifie que la zone fusionnée est valide if not ZoneEstValide(ParametresOnDrawCell.LeStringGrid,ZoneFusionnee) then Exit; //parametres d'états with ParametresOnDrawCell,ZoneFusionnee do if ((ACol=ColLeft) and (ARow=RowTop)) then State:=[]; ZoneFusionnee.State:=ZoneFusionnee.State+ParametresOnDrawCell.State; //Si la cellule courante est dans la zone de fusion, on dessine dans la fusion le texte de la cellule en haut à gauche if CelluleCouranteEstDansZone(ParametresOnDrawCell,ZoneFusionnee) then begin //Calcule le rectangle de la zone fusionnée with ZoneFusionnee,ParametresOnDrawCell do with LeStringGrid do begin ARect.TopLeft:=CalcRect(LeStringGrid,ColLeft,RowTop).TopLeft; ARect.BottomRight:=CalcRect(LeStringGrid ,ColRight,RowBottom).BottomRight; end; //on va passer les parametres de OnDrawCell avec une zone de dessin un peu élargie; DesParametres:=ParametresOnDrawCell; DesParametres.Rect:=ARect; DesParametres.ACol:=ZoneFusionnee.ColLeft; DesParametres.ARow:=ZoneFusionnee.RowTop; DesParametres.State:=ZoneFusionnee.State; DessineCellule(DesParametres, ZoneFusionnee.AlignementHorizontal, ZoneFusionnee.AlignementVertical, ZoneFusionnee.TextJustification); result:=True; end; end; end.
A partir de là Nous avons divers types énumérés, enregistrements et fonctions à notre disposition :
Pour l'alignement du texte :
Pour les paramètres à passer aux procédures/fonctions qui suivent
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3 TAlignementVertical=(alVTop,alVCenter,alVBottom); TAlignementHorizontal=(alHLeft,alHCenter,alHRight); TJustification=(JustLeft,JustCenter,JustRight);
Et des fonctions et procédures :
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 TZoneFusionnee=record ColLeft,RowTop,ColRight,RowBottom:Integer; AlignementHorizontal:TAlignementHorizontal; AlignementVertical:TAlignementVertical; TextJustification:TJustification; State:TGridDrawState; ZoneValide:Boolean; end; TParametresOnDrawCell=record LeStringGrid:TStringGrid; ACol,ARow:Integer; Rect:TRect; State:TGridDrawState; end;
Ce qu'il faut savoir, c'est que mon MergeCells d'hier s'est transformé en DessineZone (pourquoi pas ?...)
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 //utilitaire pour la visibilité du code function SetZoneFusionnee(AStringGrid:TStringGrid;ZoneText:string; ColLeft,RowTop,ColRight,RowBottom:Integer; AlignementHorizontal:TAlignementHorizontal; AlignementVertical:TAlignementVertical; TextJustification:TJustification):TZoneFusionnee; //Pour dessiner une cellule simple (utilisée par MergedCells une zone) procedure DessineCellule(ParametresOnDrawCell:TParametresOnDrawCell; AlignementHorizontal:TAlignementHorizontal; AlignementVertical:TAlignementVertical; TextJustification:TJustification); //Pour dessiner une fusion de cellules function DessineZone(ParametresOnDrawCell:TParametresOnDrawCell;var ZoneFusionnee:TZoneFusionnee):Boolean; //pour vérifiée si une cellule se trouve dans la zone fusionnee spécifiee; function (on utilise que si on a envie) CelluleEstDansZone(ACol,Arow:Integer;ZoneFusionnee:TZoneFusionnee):Boolean;
Et grosso modo, voici un exemple d'utilisation :
J'espère que c'est suffisemment parlant
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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89 unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, Grids, StdCtrls,UnitFusionStringGrid; type TForm1 = class(TForm) StringGrid1: TStringGrid; procedure StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState); procedure FormCreate(Sender: TObject); private { Déclarations privées } public { Déclarations publiques } //déclaration de nos différentes zones zone1,zone2,zone3,zone4:TZoneFusionnee; end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState); var s:string; TheStringGrid:TStringGrid; DesParametres:TParametresOnDrawCell; begin //On récupère les Paramêtres de OnDrawCell DesParametres.ACol:=ACol; DesParametres.ARow:=ARow; DesParametres.Rect:=Rect; DesParametres.State:=State; //Quel est le StringGrid qui déclenche l'évènement ? DesParametres.LeStringGrid:=(sender as TStringGrid); //On dessine la ou les cellules //d'abord on teste et s'occupe des fusions de cellule //(on peut faire une boucle sur un tableau de zones fusionnées aussi...) if not DessineZone(DesParametres,zone1) then if not DessineZone(DesParametres,zone2) then if not DessineZone(DesParametres,zone3) then if not DessineZone(DesParametres,zone4) then begin //puis si on a pas une cellule fusionnée on la dessine normalement if (ACol=1) and (ARow=2) then begin StringGrid1.Canvas.Brush.Color:=clYellow; StringGrid1.Canvas.Font.Color:=clRed; end; DessineCellule(DesParametres,alHCenter,alVCenter,JustCenter); end; end; procedure TForm1.FormCreate(Sender: TObject); begin //On fait ce qui suit sinon, ce n'est pas beau StringGrid1.ColCount:=5; StringGrid1.RowCount:=5; StringGrid1.FixedRows:=0; StringGrid1.FixedCols:=0; //on a defini nos zones ici, mais ailleurs, c'est faisable :) //Un titre zone1:=SetZoneFusionnee(StringGrid1,'Un Titre qui en dit long...',0,0,5,0,alHCenter,alVCenter,JustCenter); //du multiligne zone2:=SetZoneFusionnee(StringGrid1,'Salut'+#13#10+'Delphi',2,2,2,3,alHCenter,alVCenter,JustCenter); //multiligne aligné à droite ! (l'ensemble du texte est centré) zone3:=SetZoneFusionnee(StringGrid1,'Salut'+#13#10+'Delphi',4,1,4,3,alHCenter,alVCenter,JustRight); //ligne alignée bien à gauche zone4:=SetZoneFusionnee(StringGrid1,'Bonjour à tout le monde',1,4,3,4,alHLeft,alVCenter,JustLeft); Stringgrid1.Cells[3,4]:='du texte qui n''apparait pas (masqué par une zone)'; Stringgrid1.Cells[1,1]:='Texte normal qui déborde de la cellule :)'; Stringgrid1.Cells[1,2]:='Coucou :)'; end; end.
Maintenant, il faut que j'adapte ton code pour ton besoin, à moins que tu n'y arrive tous seul ?
J'ai adapté ton code, j'espère que c'est ce que tu voulais faire
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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87 unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, Grids, StdCtrls,UnitFusionStringGrid; const NbreColonnes=20; type TForm1 = class(TForm) StringGrid1: TStringGrid; procedure StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState); procedure FormCreate(Sender: TObject); private { Déclarations privées } public { Déclarations publiques } zones:array[0..NbreColonnes-1] of TZoneFusionnee; Nbre:Integer; end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState); var DesParametres:TParametresOnDrawCell; OnADessineDansUneZone:Boolean; begin //On récupère les Paramêtres de OnDrawCell DesParametres.ACol:=ACol; DesParametres.ARow:=ARow; DesParametres.Rect:=Rect; DesParametres.State:=State; //Quel est le StringGrid qui déclenche l'évènement ? DesParametres.LeStringGrid:=(sender as TStringGrid); //On dessine la ou les cellules //d'abord on teste et s'occupe des fusions de cellule //(on peut faire une boucle sur un tableau de zones fusionnées aussi...) //d'abord on teste et on s'occupe des fusions de cellule OnADessineDansUneZone:=False; Nbre:=0; while ((Nbre<DesParametres.LeStringGrid.ColCount) and (not OnADessineDansUneZone)) do begin OnADessineDansUneZone:=DessineZone(DesParametres,zones[Nbre]); Nbre:=Nbre+5; end; if not OnADessineDansUneZone then begin //puis si on a pas une cellule fusionnée on la dessine normalement DessineCellule(DesParametres,alHCenter,alVCenter,JustCenter); end; end; procedure TForm1.FormCreate(Sender: TObject); var LaDate:TdateTime; begin StringGrid1.ColCount:=NbreColonnes; StringGrid1.RowCount:=5; StringGrid1.FixedRows:=0; StringGrid1.FixedCols:=0; StringGrid1.DefaultColWidth:=40; //on a defini nos zones ici, mais ailleurs, c'est faisable :) Nbre:=0; LaDate:=EncodeDate(2006,02,07); while Nbre<stringgrid1.ColCount do begin zones[Nbre]:=SetZoneFusionnee(StringGrid1,DateToStr(LaDate),Nbre,0,Nbre+5-1,0,alHCenter,alVCenter,JustCenter); Stringgrid1.Cells[Nbre,1]:='Matin'; Stringgrid1.Cells[1+Nbre,1]:='Midi'; Stringgrid1.Cells[2+Nbre,1]:='16h'; Stringgrid1.Cells[3+Nbre,1]:='Soir'; Stringgrid1.Cells[4+Nbre,1]:='Nuit'; LaDate:=LaDate+1; Nbre:=Nbre+5; end; end; end.
Et sinon, mon code n'est pas du tout (mais alors pas du tout) adapté lorsque dans le StringGrid :
- Il y a des Scrollbars et on joue avec..
Ne gère pas :
Les rectangles de focalisation, le rendu des lignes et colonnes fixes... que Delphi s'empresse de dessiner par dessus mon dessin.
il y a du code à rajouter, je vois bien où, mais ça ferait trop usine à gaz pour le forum , même que ça fait déjà beaucoup là...)
Maintenant, il faut que j'adapte ton code pour ton besoin, à moins que tu n'y arrive tous seul ?
Rigolo, fastoche ton code ...
Plus sérieusement, je vais commencer à l'etudier ...
Salut,
Je ne t'ai pas oublé ... Ton prog marche bien, le resultat à l'air propre, mais je pensais qu'avec une dizaine de lignes on pouvait faire ce qu'on voulait.
Et vu que la fusion de mon stringgrid est le resultat final de tous mes calculs, ..., alors avant de faire joli, on travaille le code. De plus, on a pas le meme niveau. ca fait deux mois que j'y suis dessus, débutant et 2-3 h par semaine, ca n'avance pas bien vite ...
Tant fait pas, le moment venu, je saurai me rappler de tes bons et loyaux services ....
Amicalement
Bonjour,
Je suis une jeune étudiante et je dois développer un agenda en delphi pour un stage. L'utilisatrice a des rendez-vous de diverses durées de 15 minutes à plus. J'ai un stringrid composé de 5 colones et de 48 lignes (de 8h à 20h tout les 15 minutes) j'enregistre les rendez-vous dans une base de donnée mysql. J'ai besoin de fussionner des cellules en fonction de la durée des rendez-vous.
C'est pourquoi j'ai étudié attentivement le code de waskol. Je le trouve vraiment adapter à mon besoin bravo Waskol .
Par contre, je ne sais pas à l'avance combien j'ai de zones à fussionner, je ne peux donc pas déclarer les variables de type TZoneFussionnee. J'ai essayé de les créer dynamiquement comme on le fais pour les composants visuels : MaZone := TZoneFussionnee.create(self) mais cela ne fonctionne pas.
Quelqu'un aurait-il une idée svp?
Désolé si mon message est long, c'est mon premier
Vous avez un bloqueur de publicités installé.
Le Club Developpez.com n'affiche que des publicités IT, discrètes et non intrusives.
Afin que nous puissions continuer à vous fournir gratuitement du contenu de qualité, merci de nous soutenir en désactivant votre bloqueur de publicités sur Developpez.com.
Partager