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:
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;
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
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);
}
En espérant que ça vous serve autant qu'à moi...