Bonjour à tous,
J'ai cru devenir fou en essayant de résoudre ce problème tout seul... et j'ai abandonné !
Je publie ici une solution qui peut servir à pas mal de monde pour fusionner des cellules (fixes) dans un TStringGrid. Elle est l'oeuvre du génial CapJack (chapeau!) sur le forum Delphi (voyez ici: http://www.developpez.net/forums/d32...s/#post2110839), et je me suis borné à la traduire en C++ Builder.
Code original pour Delphi:
Traduction C++ Builder:
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 //Code Original CapJack pour Delphi // Renvoie true si (C,R) est valide et dans le rectangle supérieur gauche fixe function SGFixedTopLeft(const SG:TStringGrid;const C,R:Integer):Boolean; begin Result := (C >= 0) and (C < SG.FixedCols) and (R >= 0) and (R < SG.FixedRows) end; // Renvoie true si (C,R) est valide et dans le rectangle supérieur fixe verticalement function SGFixedTop(const SG:TStringGrid;const C,R:Integer):Boolean; begin Result := (C >= SG.FixedCols) and (C < SG.ColCount) and (R >= 0) and (R < SG.FixedRows) end; // Renvoie true si (C,R) est valide et dans le rectangle gauche fixe horizontalement function SGFixedLeft(const SG:TStringGrid;const C,R:Integer):Boolean; begin Result := (C >= 0) and (C < SG.FixedCols) and (R >= SG.FixedRows) and (R < SG.RowCount) end; // Renvoie true si (C,R) est valide et dans le rectangle non fixe function SGNotFixed(const SG:TStringGrid;const C,R:Integer):Boolean; begin Result := (C >= SG.FixedCols) and (C < SG.ColCount) and (R >= SG.FixedRows) and (R < SG.RowCount) end; function DrawMergedCells(const ASG:TStringGrid;const ACol,ARow,ACol1,ARow1,ACol2,ARow2:Integer):Boolean; var C1,R1,C2,R2,C,R:Integer; Fixed : Boolean; DR : TRect; // Rectangle à dessiner begin with ASG, Canvas do begin //Initialisations diverses Result := False; C1 := ACol1;R1 := ARow1;C2 := ACol2;R2 := ARow2; if C1 < 0 then C1 := 0; if C2 >= ColCount then C2 := ColCount-1; if R1 < 0 then C1 := 0; if R2 >= RowCount then C2 := RowCount-1; if (C1 <= C2) and (R1 <= R2) and (ACol>=C1) and (ACol<=C2) and (ARow>=R1) and (ARow<=R2) then begin // Deuxième série de validations. La zone fusionnée doit être comprise // toute entière dans l'une des quatre zones définies plus haut // pour être logiquement valide. if ( SGFixedTopLeft(ASG,C1,R1) and SGFixedTopLeft(ASG,C2,R2) ) or ( SGFixedTop(ASG,C1,R1) and SGFixedTop(ASG,C2,R2) ) or ( SGFixedLeft(ASG,C1,R1) and SGFixedLeft(ASG,C2,R2) ) then Fixed := True else if ( SGNotFixed(ASG,C1,R1) and SGNotFixed(ASG,C2,R2) ) then Fixed := False else Exit; // Si DrawMergedCells est appelée, cela signifie que la cellule // concernée par l'affichage est visible, donc CellRect // Renvoie les vraies coordonnées écran de la cellule concernée. DR := CellRect(ACol,ARow); // Maintenant on agrandit le rectangle de la zone de fusion, // en partant de la cellule courante, donc sans faire d'appels // à CellRect qui risqueraient de renvoyer des rectangles nuls. DR.Right := DR.Left + ColWidths[ACol]; for C := ACol - 1 downto C1 do DR.Left := DR.Left - ColWidths[C] - GridLineWidth; for C := ACol + 1 to C2 do DR.Right := DR.Right + ColWidths[C] + GridLineWidth; DR.Bottom := DR.Top + RowHeights[ARow]; for R := ARow - 1 downto R1 do DR.Top := DR.Top - RowHeights[R] - GridLineWidth; for R := ARow + 1 to R2 do DR.Bottom := DR.Bottom + RowHeights[R] + GridLineWidth; if Fixed then begin FillRect(DR); // Le bloc qui suit pour imiter le "look" par défaut if GridLineWidth = 1 then begin Pen.Color := clBlack;Pen.Width := 1; Canvas.Rectangle(DR.Left-1,DR.Top-1,DR.Right+1,DR.Bottom+1); end; // Frame3D(ASG.Canvas, DR, clBtnHighlight, clBtnShadow, BevelWidth); InflateRect(DR,-2,-2); DrawText(ASG.Canvas.Handle, PChar(Cells[ACol1,ARow1]), -1, DR ,DT_CENTER or DT_NOPREFIX or DT_VCENTER or DT_SINGLELINE ); Result := True; end else begin // TODO... cellules non-fixes fusionnées end; end; end; end; procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState); begin with Sender as TStringGrid do if not DrawMergedCells(Sender as TStringGrid,ACol,ARow,1,0,3,0) and not DrawMergedCells(Sender as TStringGrid,ACol,ARow,4,0,6,0) then DrawMergedCells(Sender as TStringGrid,ACol,ARow,ACol,ARow,ACol,ARow); end;
En espérant que ça vous serve autant qu'à moi...
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 //Traduction C++ Builder 6 par mes humbles soins //Déclarations Fonctions pour la fusion des cellules StringGrid bool GrilleFixedTopLeft(TStringGrid* Grille,int const C, int const R); bool GrilleFixedTop(TStringGrid* Grille, int const C, int const R); bool GrilleFixedLeft(TStringGrid* Grille, int const C,int const R); bool GrilleNotFixed(TStringGrid* Grille, int const C, int const R); //Fonctions pour la fusion des cellules StringGrid // Renvoie true si (C,R) est valide et dans le rectangle supérieur gauche fixe bool GrilleFixedTopLeft(TStringGrid* Grille, int const C, int const R) { return ((C >= 0) && (C < Grille->FixedCols) && (R >= 0) && (R < Grille->FixedRows)); } // Renvoie true si (C,R) est valide et dans le rectangle supérieur fixe verticalement bool GrilleFixedTop(TStringGrid* Grille, int const C, int const R) { return ((C >= Grille->FixedCols) && (C < Grille->ColCount) && (R >= 0) && (R < Grille->FixedRows)); } // Renvoie true si (C,R) est valide et dans le rectangle gauche fixe horizontalement bool GrilleFixedLeft(TStringGrid* Grille, int const C,int const R) { return ((C >= 0) && (C < Grille->FixedCols) && (R >= Grille->FixedRows) && (R < Grille->RowCount)); } // Renvoie true si (C,R) est valide et dans le rectangle non fixe bool GrilleNotFixed(TStringGrid* Grille, int const C, int const R) { return ((C >= Grille->FixedCols) && (C < Grille->ColCount) && (R >= Grille->FixedRows) && (R < Grille->RowCount)); } DrawMergedCells(TStringGrid* Grille,int const ACol, int const ARow, int const ACol1, int const ARow1, int const ACol2, int const ARow2) { int C1,R1,C2,R2,C,R; bool Fixed, Result; TRect DR; // Rectangle à dessiner //Initialisations diverses Result= false; C1 = ACol1;R1 = ARow1;C2 = ACol2;R2 = ARow2; if(C1 < 0) C1 = 0; if(C2 >= Grille->ColCount) C2 = Grille->ColCount-1; if(R1 < 0) C1 = 0; if(R2 >= Grille->RowCount) C2 = Grille->RowCount-1; if ((C1 <= C2) && (R1 <= R2) && (ACol>=C1) && (ACol<=C2) && (ARow>=R1) && (ARow<=R2)) { // Deuxième série de validations. La zone fusionnée doit être comprise // toute entière dans l'une des quatre zones définies plus haut // pour être logiquement valide. if ((GrilleFixedTopLeft(Grille,C1,R1) && GrilleFixedTopLeft(Grille,C2,R2)) || ( GrilleFixedTop(Grille,C1,R1) && GrilleFixedTop(Grille,C2,R2)) || ( GrilleFixedLeft(Grille,C1,R1) && GrilleFixedLeft(Grille,C2,R2))) Fixed = true; else if (GrilleNotFixed(Grille,C1,R1) && GrilleNotFixed(Grille,C2,R2)) Fixed = false; else exit; // Si DrawMergedCells est appelée, cela signifie que la cellule // concernée par l'affichage est visible, donc CellRect // Renvoie les vraies coordonnées écran de la cellule concernée. DR = Grille->CellRect(ACol,ARow); // Maintenant on agrandit le rectangle de la zone de fusion, // en partant de la cellule courante, donc sans faire d'appels // à CellRect qui risqueraient de renvoyer des rectangles nuls. DR.Right = DR.Left + Grille->ColWidths[ACol]; for(C=ACol-1;C>=C1;C--) DR.Left = DR.Left - Grille->ColWidths[C] - Grille->GridLineWidth; for(C=ACol+1;C<=C2;C++) DR.Right = DR.Right + Grille->ColWidths[C] + Grille->GridLineWidth; DR.Bottom = DR.Top + Grille->RowHeights[ARow]; for(R=ARow-1;R>=R1;R--) DR.Top = DR.Top - Grille->RowHeights[R] - Grille->GridLineWidth; for(R=ARow+1;R<=R2;R++) DR.Bottom = DR.Bottom + Grille->RowHeights[R] + Grille->GridLineWidth; if(Fixed) { Grille->Canvas->FillRect(DR); // Le bloc qui suit pour imiter le "look" par défaut if(Grille->GridLineWidth == 1) { Grille->Canvas->Pen->Color = clBlack; Grille->Canvas->Pen->Width = 1; Grille->Canvas->Rectangle(DR.Left-1,DR.Top-1,DR.Right+1,DR.Bottom+1); } // Frame3D(Grille->Canvas, DR, clBtnHighlight, clBtnShadow, Grille->Canvas->Pen->Width); InflateRect(&DR,-2,-2); DrawText(Grille->Canvas->Handle,Grille->Cells[ACol1][ARow1].c_str(), -1, &DR ,DT_CENTER || DT_NOPREFIX || DT_VCENTER || DT_SINGLELINE ); Result = true; } else { // TODO... cellules non-fixes fusionnées } } return(Result); } void __fastcall TForm1::StringGrid1TGDrawCell(TObject *Sender, int ACol, int ARow, TRect &Rect, TGridDrawState State) //Fusion cellules Ligne 0 Colonnes 1 à 3 et Ligne 0 Colonnes 4 à 6 { if(!DrawMergedCells((TStringGrid*)Sender,ACol,ARow,1,0,3,0) && !DrawMergedCells((TStringGrid*)Sender,ACol,ARow,4,0,6,0)) DrawMergedCells((TStringGrid*)Sender,ACol,ARow,ACol,ARow,ACol,ARow); }
Partager