FAQ DelphiConsultez toutes les FAQ
Nombre d'auteurs : 124, nombre de questions : 934, dernière mise à jour : 23 octobre 2024 Ajouter une question
Cette FAQ a été réalisée à partir des questions fréquemment posées sur les forums Delphi et Delphi et bases de données de www.developpez.com et de l'expérience personnelle des auteurs.
Nous tenons à souligner que cette FAQ ne garantit en aucun cas que les informations qu'elle propose soient correctes. Les auteurs font le maximum, mais l'erreur est humaine. Cette FAQ ne prétend pas non plus être complète. Si vous souhaitez y apporter des corrections ou la compléter, contactez un responsable (lien au bas de cette page).
Nous espérons que cette FAQ saura répondre à vos attentes. Nous vous en souhaitons une bonne lecture.
L'équipe Delphi de Developpez.com.
- Comment supprimer n'importe quelle ligne d'un TStringGrid ?
- Comment supprimer une ligne ou une colonne dans un TStringGrid ?
- Comment centrer le texte d'un TStringGrid ?
- Comment avoir des lignes de couleur sur un TStringGrid ?
- Comment mettre une image en fond d'un TStringGrid ?
- Comment trier une grille en fonction d'une colonne ?
- Comment exporter un TStringGrid vers Excel ?
- Comment avoir des retours chariot dans une cellule d'un TStringGrid ?
- Comment sélectionner du texte dans une cellule de TStringGrid ?
- Comment empêcher l'édition d'une cellulle dans un TStringGrid ?
- Comment charger/sauver un TStringGrid dans un fichier ?
- Comment déplacer une ligne dans un TStringGrid ?
- Comment fusionner les cellules d'un StringGrid ?
- Comment ajouter un bouton dans un TStringGrid ?
- Accélération de l'affichage d'un TStringGrid
- Comment faire le nettoyage d'une StringGrid ?
- Comment gérer les déplacements avec les flèches D et G et sauter les Cells.ColWidths=-1
- Comment ajouter une TComboBox dans un TStringGrid ?
Le composant StringGrid ne permet pas de supprimer une ligne directement avec son numéro. Il faut donc copier chaque ligne située après celle à supprimer sur la ligne qui la précède, puis supprimer la dernière ligne.
Par exemple, si on veut supprimer la ligne 3, on copie la 4 sur la 3, la 5 sur la 4, ... et on supprime la dernière.
On peut donc utiliser une procédure telle que :
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 | procedure RemoveLine(AStringGrid: TStringGrid; ALine: Integer); var i: Integer; begin for i := ALine to AStringGrid.RowCount - 2 do AStringGrid.Rows[i] := AStringGrid.Rows[i + 1]; AStringGrid.RowCount := AStringGrid.RowCount - 1; end; |
Code delphi : | Sélectionner tout |
RemoveLine(MaStringGrid,2);
Il suffit de rendre publiques les méthodes DeleteRow et DeleteColumn de la classe TCustomGrid dont hérite la classe TStringGrid à travers le transtypage de cette dernière en une classe dérivée déclarée dans la même unité :
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 | type TPublicStringGrid = class(TCustomGrid); ... procedure TForm1.Button1Click(Sender: TObject); begin //supprime la ligne n°2 (donc la troisième ligne, la première étant la ligne n°0) : TPublicStringGrid(StringGrid1).DeleteRow(2); //Supprime la deuxième colonne (colonne n°1) : TPublicStringGrid(StringGrid1).DeleteColumn(1); end; |
Le code suivant permet de centrer le texte dans les cellules d'un TStringGrid. Le centrage est à la fois vertical et horizontal. Le dessin est ici effectué avec l'API de Windows car la fonction TextOut du canevas des composants ne permet pas directement le centrage.
Code delphi : | Sélectionner tout |
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 | procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState); begin With Sender As TStringGrid Do With Canvas Do Begin { Sélection de la couleur de fond } If gdFixed in State Then Brush.Color := clBtnFace Else If gdSelected In State Then Brush.Color := clNavy Else Brush.Color := clWhite; { Dessin du fond } FillRect(Rect); { Sélection de la couleur de texte } If gdSelected In State Then SetTextColor(Canvas.Handle,clWhite) Else SetTextColor(Canvas.Handle,clBlack); { Dessin du texte en utilisant la fonction API } DrawText(Canvas.Handle, PChar(Cells[ACol,ARow]), -1, Rect , DT_CENTER or DT_NOPREFIX or DT_VCENTER or DT_SINGLELINE ); End; end; |
Une variante de ce code permet d'écrire le texte d'une cellule sur plusieurs lignes :
Code delphi : | Sélectionner tout |
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 | procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState); begin With Sender As TStringGrid Do With Canvas Do Begin { Sélection de la couleur de fond } If gdFixed in State Then Brush.Color := clBtnFace Else If gdSelected In State Then Brush.Color := clNavy Else Brush.Color := clWhite; { Dessin du fond } FillRect(Rect); { Sélection de la couleur de texte } If gdSelected In State Then SetTextColor(Canvas.Handle,clWhite) Else SetTextColor(Canvas.Handle,clBlack); { Dessin du texte en utilisant la fonction API } DrawText(Canvas.Handle, PChar(Cells[ACol,ARow]), -1, Rect , DT_CENTER or DT_NOPREFIX or DT_WORDBREAK ); End; end; |
Ce code permet d'obtenir une grille avec des lignes de couleurs différentes. Ceci facilite la lecture des grilles de taille importantes. Pour utiliser ce code, il faut l'ajouter dans l'évènement OnDrawCell du TStringGrid :
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState); begin With Sender As TStringGrid Do With Canvas Do Begin { sélection de la couleur de fond } If gdFixed in State Then Brush.Color := clBtnFace Else If gdSelected In State Then Brush.Color := clNavy Else If Odd(ARow) Then Brush.Color := $FFE0FF Else Brush.Color := $FFFFE0; { Dessin du fond } FillRect(Rect); { Sélection de la couleur d'écriture } If gdSelected In State Then Font.Color:=clWhite Else Font.Color:=clBlack; { Dessin du texte } TextOut(Rect.Left,Rect.Top,Cells[ACol,ARow]); End; end; |
Voici le code à appliquer pour mettre une image en fond des cellules blanches d'un TStringGrid. L'image est contenue dans une Bitmap, mais elle peut aussi être contenu dans un TImage. Cette procédure gère le déplacement du fond de l'image si l'utilisateur se sert des barres de défilement :
Code delphi : | Sélectionner tout |
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 | procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState); Var i,j,x,y:Integer; R:TRect; begin With Sender As TStringGrid Do With Canvas Do Begin If gdFixed in State Then Begin { Les cellules fixes sont toujours dessinées en gris } Brush.Color := clBtnFace; Brush.Style := bsSolid; FillRect(Rect); End Else Begin If gdSelected In State Then Begin { Les cellules sélectionnées sont en bleue } Brush.Color := clNavy; Brush.Style := bsSolid; FillRect(Rect); End Else Begin { Recherche de la zone image à copier pour tenir compte des décalages } { de la grille en fonction des barres de défilement. } X:=0; For i:=FixedCols+1 To ACol Do Inc(x,ColWidths [i]); Y:=0; For i:=FixedRows+1 To ARow Do Inc(Y,RowHeights[i]); R.Left :=X; R.Right :=X+Rect.Right-Rect.Left; R.Top :=Y; R.Bottom :=Y+Rect.Bottom-Rect.Top; { Dessin d'une partie de l'image } CopyRect(Rect,MonBitmap.Canvas,R); Brush.Style := bsClear; End; End; { Sélection de la couleur de texte } If gdSelected In State Then SetTextColor(Canvas.Handle,clWhite) Else SetTextColor(Canvas.Handle,clBlack); { Dessin du texte en utilisant la fonction API } DrawText(Canvas.Handle, PChar(Cells[ACol,ARow]), -1, Rect ,DT_NOPREFIX ); End; end; |
On a souvent besoin de trier le contenu d'un composant TStringGrid afin de présenter des données à l'utilisateur. Malheureusement, ce composant ne propose pas de fonction de tri. Voici donc un source démontrant comment implémenter cette fonctionnalité.
Voici une procédure qui vous permet d'exporter le contenu d'un TStringGrid vers Excel.
Code delphi : | Sélectionner tout |
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 | procedure XlsWriteCellLabel(XlsStream: TStream; const ACol, ARow: Word; const AValue: string); var L: Word; const {$J+} CXlsLabel: array[0..5] of Word = ($204, 0, 0, 0, 0, 0); {$J-} begin L := Length(AValue); CXlsLabel[1] := 8 + L; CXlsLabel[2] := ARow; CXlsLabel[3] := ACol; CXlsLabel[5] := L; XlsStream.WriteBuffer(CXlsLabel, SizeOf(CXlsLabel)); XlsStream.WriteBuffer(Pointer(AValue)^, L); end; function SaveAsExcelFile(AGrid: TStringGrid; AFileName: string): Boolean; const {$J+} CXlsBof: array[0..5] of Word = ($809, 8, 00, $10, 0, 0); {$J-} CXlsEof: array[0..1] of Word = ($0A, 00); var FStream: TFileStream; I, J: Integer; begin FStream := TFileStream.Create(PChar(AFileName), fmCreate or fmOpenWrite); try CXlsBof[4] := 0; FStream.WriteBuffer(CXlsBof, SizeOf(CXlsBof)); for i := 0 to AGrid.ColCount - 1 do for j := 0 to AGrid.RowCount - 1 do XlsWriteCellLabel(FStream, I, J, AGrid.cells[i, j]); FStream.WriteBuffer(CXlsEof, SizeOf(CXlsEof)); Result := True; finally FStream.Free; end; end; |
Utilisation :
Code delphi : | Sélectionner tout |
SaveAsExcelFile(StringGrid1,'c:\testxls.xls');
Pour cela, il faut activer l'option goAlwaysShowEditor et ajouter le code suivant dans l'événement OnKeyDown :
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 | procedure TForm1.StringGrid1KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin if Key = VK_RETURN then StringGrid1.Cells[StringGrid1.Col, StringGrid1.Row] := StringGrid1.Cells[StringGrid1.Col, StringGrid1.Row] + sLineBreak; end; |
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState); begin with Sender as TStringGrid do with Canvas do begin { Sélection de la couleur de fond } if gdFixed in State then Brush.Color := clBtnFace else if gdSelected in State then Brush.Color := clNavy else Brush.Color := clWhite; { Dessin du fond } FillRect(Rect); { Sélection de la couleur de texte } if gdSelected in State then SetTextColor(Canvas.Handle,clWhite) else SetTextColor(Canvas.Handle,clBlack); { Dessin du texte en utilisant la fonction API } DrawText(Canvas.Handle, PChar(Cells[ACol,ARow]), -1, Rect ,DT_NOPREFIX or DT_WORDBREAK ); end; end; |
Pour sélectionner du texte dans une cellule du composant TStringGrid, nous utilisons le composant TInplaceEdit spécialisé pour le traitement du texte des cellules des grilles. Il s'agit en réalité d'une fiche sans bordure dont le parent est la grille; c'est ce qui explique pourquoi il est invisible et n'apparaît pas en tant que contrôle indépendant.
Code delphi : | Sélectionner tout |
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 | procedure TForm1.Button1Click(Sender: TObject); //On parcours les contrôles enfants de la grille à la recherche du TInplaceEdit function GetInPlaceEdit(ACustomGrid: TCustomGrid): TInplaceEdit; var i: integer; begin for i:=0 to ACustomGrid.ControlCount-1 do if ACustomGrid.Controls[i] is TInplaceEdit then begin result:= TInplaceEdit(ACustomGrid.Controls[i]); exit; end; result:= nil; end; var myRect: TGridRect; ed: TInplaceEdit; begin // Sélection de la cellule myRect.Left := 1; myRect.Top := 2; myRect.Right := 1; myRect.Bottom := 2; sg.Selection := myRect; // Donner le focus à la grille ActiveControl:= sg; // Passer la grille en mode édition sg.EditorMode:= true; // Récupérer l'éditeur ed:= GetInPlaceEdit(sg); // Et en faire quelque chose // dans ce cas sélectionner le texte contenu dans la cellule if ed <> nil then begin ed.SelStart:= 0; ed.SelLength:= ed.GetTextLen; end; end; |
Nous souhaitons que l'utilisateur n'ait pas la possibilité d'éditer certaines cellules d'un TStringGrid, les autres restant parfaitement éditables.
C'est la propriété Options-->goEditing qui permet ou non l'édition des cellules dans le TStringGrid.
Il suffit alors, dans l'évènement OnSelectCell, d'activer ou de désactiver l'option goEditing en fonction des coordonnées de la cellule et/ou de ce qu'elle contient :
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 7 | procedure TForm1.StringGrid1SelectCell(Sender: TObject; ACol, ARow: Integer; var CanSelect: Boolean); begin if (ARow=1) //Condition à remplir pour interdire l'édition de la cellule then StringGrid1.Options:=StringGrid1.Options-[goEditing] //désactive l'édition else StringGrid1.Options:=StringGrid1.Options+[goEditing]; //active l'édition end; |
Nous vous proposons ici deux procédures/fonctions toutes faites qui le permettent :
Première méthode (routines fichiers de bas niveau) :
Code delphi : | Sélectionner tout |
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 | (* Sauver un TStringGrid dans un fichier *) procedure SaveStringGrid(StringGrid: TStringGrid; const FileName: TFileName); var f: TextFile; i, k: Integer; begin AssignFile(f, FileName); Rewrite(f); with StringGrid do begin // Ecrire le nombre de Colonnes/lignes Writeln(f, ColCount); Writeln(f, RowCount); // Boucler sur les cellules for i := 0 to ColCount - 1 do for k := 0 to RowCount - 1 do Writeln(F, Cells[i, k]); end; CloseFile(F); end; // Charger un TStringGrid depuis un fichier // Attention, il faut tester l'existence du fichier avant d'utiliser cette procédure procedure LoadStringGrid(StringGrid: TStringGrid; const FileName: TFileName); var f: TextFile; iTmp, i, k: Integer; strTemp: String; begin AssignFile(f, FileName); Reset(f); with StringGrid do begin // Obtenir le nombre de colonnes Readln(f, iTmp); ColCount := iTmp; // et de lignes Readln(f, iTmp); RowCount := iTmp; // remplir les cellules for i := 0 to ColCount - 1 do for k := 0 to RowCount - 1 do begin Readln(f, strTemp); Cells[i, k] := strTemp; end; end; CloseFile(f); end; |
Code delphi : | Sélectionner tout |
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 | //écrire une chaîne de caractères depuis le flux procedure WriteStreamString(Stream : TStream; UneChaine : string); var LongueurChaine : integer; begin //obtenir la longueur de la chaîne de caractères LongueurChaine := Length(UneChaine); //écrire cette longueur dans le flux Stream.Write(LongueurChaine,SizeOf(integer)); //écrire les caractères Stream.Write(UneChaine[1], LongueurChaine); end; //retourne une chaîne de caractères depuis le flux function ReadStreamString(Stream : TStream) : string; var LongueurChaine : integer; begin //obtenir la longueur de la chaîne de caractères Stream.Read(LongueurChaine,SizeOf(integer)); //Redimensionner la chaine pour allouer la mémoire nécessaire SetLength(Result, LongueurChaine); //Lire les caractères Stream.Read(Result[1], LongueurChaine); end; // Sauver un TStringGrid dans un fichier procedure SaveStringGrid(StringGrid: TStringGrid; const FileName: TFileName); var f:TFileStream; i, k ,cc, rr: Integer; begin if FileExists(FileName) then f:=TFileStream.Create(FileName,fmOpenWrite) else f:=TFileStream.Create(FileName,fmCreate); try with StringGrid do begin cc:=ColCount; rr:=RowCount; // Ecrire le nombre de Colonnes/lignes f.Write(cc,SizeOf(integer)); f.Write(rr,SizeOf(integer)); // Boucler sur les cellules for i := 0 to ColCount - 1 do for k := 0 to RowCount - 1 do WriteStreamString(F,Cells[i, k]); end; finally F.Free; end; end; // Charger un TStringGrid depuis un fichier function LoadStringGrid(StringGrid: TStringGrid; const FileName: TFileName):boolean; var f:TFileStream; iTmp, i, k, cc, rr: Integer; begin result:=FileExists(FileName); if not result then Exit; f:=TFileStream.Create(FileName,fmOpenRead); try with StringGrid do begin // Obtenir le nombre de colonnes f.Read(iTmp,SizeOf(integer)); ColCount := iTmp; // et de lignes f.Read(iTmp,SizeOf(integer)); RowCount := iTmp; // remplir les cellules for i := 0 to ColCount - 1 do for k := 0 to RowCount - 1 do Cells[i, k] := ReadStreamString(f); end; finally F.Free; end; end; |
Il suffit de rendre publiques les méthodes MoveRow et MoveColumn de la classe TCustomGrid dont hérite la classe TStringGrid à travers le transtypage de cette dernière en une classe dérivée déclarée dans la même unité :
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 | type TPublicStringGrid = class(TCustomGrid); ... procedure TForm1.Button1Click(Sender: TObject); begin //Déplace la première ligne (Row=0) vers la seconde (Row=1) : TPublicStringGrid(StringGrid1).MoveRow(0,1); //Déplace la première colonne (Col=0) vers la seconde (Col=1) : TPublicStringGrid(StringGrid1).MoveColumn(0,1); end; |
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 est déclenché 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 :
Code delphi : | Sélectionner tout |
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 | function MergedCells(AStringGrid:TStringGrid; CurrentCol,CurrentRow,Col1,Row1,Col2,Row2:Integer; CurrentState: TGridDrawState):Boolean; var 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 que 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]); 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; |
Code delphi : | Sélectionner tout |
1 2 | MergedCells(AStringGrid:TStringGrid;CurrentCol,CurrentRow,Col1,Row1,Col2,Row2:Integer; CurrentState: TGridDrawState):Boolean; |
- Elle s'utilise dans la méthode de l'évènement StringGrid1.OnDrawCell
Code delphi : | Sélectionner tout |
1 2 | procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState); |
- AStringGrid est le StringGrid concerné par la fusion de cellule, (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 notre fiche, nous avons juste mis un TStringGrid et ce code :
Code delphi : | Sélectionner tout |
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 | 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 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 que 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]); 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(les) cellule(s) //d'abord on teste et on s'occupe des fusions de cellules 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. |
Code delphi : | Sélectionner tout |
procedure TForm1.FormCreate(Sender: TObject);
Ainsi que :
Code delphi : | Sélectionner tout |
1 2 | procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState); |
Code Delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 | //dans le formcreate MaStringGrid.DoubleBuffered := True; MaStringGrid.ControlStyle := StringGrid2.ControlStyle + [csClickEvents]; ... // dans le ondrawcell du stringGrid // la condition peut etre un numero de colonne ou de ligne if maCondition Then if (csClicked in (sender as TstringGrid).ControlState) Then DrawFrameControl((sender as TstringGrid).Canvas.Handle, Rect,DFC_BUTTON, DFCS_BUTTONPUSH or DFCS_PUSHED ) else DrawFrameControl((sender as TstringGrid).Canvas.Handle, Rect, FC_BUTTON, DFCS_BUTTONPUSH ) |
Les méthodes BeginUpdate et EndUpdate n'existent pas pour les TStringGrids. Il existe seulement Grid.Rows[i].BeginUpdate et Grid.Rows[i].EndUpdate, pour le faire ligne par ligne.
Pour accélérer l'affichage des TStringGrids comportant un grand nombre de lignes, et aussi pour éviter le scintillement à l'affichage, on peut évidement utiliser BeginUpdate et EndUpdate par ligne.
Mais cette méthode est peu efficace. On peut donc utiliser la méthode suivante qui divise le temps d'affichage par 2 ou 3 :
Code Delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | { ====================================================================== } procedure LockGrid(Grid: TStringGrid); // Blocage de la mise à jour de l'affichage de la StringGrid begin Grid.Perform(WM_SETREDRAW, 0, 0); end; { ====================================================================== } procedure UnLockGrid(Grid: TStringGrid); // Déblocage de la mise à jour de l'affichage de la StringGrid begin Grid.Perform(WM_SETREDRAW, 1, 0); Grid.Invalidate; end; { ===================================================================== } |
Utilisation :
Code Delphi : | Sélectionner tout |
1 2 3 | LockGrid(Grille) ; // Blocage de l'affichage // Remplissage de la grille ... UnLockGrid(Grille) ; // Déblocage de l'affichage |
Testé avec D7 et D10.3
Avec ce code ...
Code Delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 | var I:integer; begin // vide le composant for i:=0 to StringGrid.RowCount -1 do StringGrid.rows[i].Clear; // on met RowCount et éventuellement ColCount à leur valeur minimale = 1 StringGrid.RowCount:=1; StringGrid.ColCount:=1; |
L'idée est de se déplacer dans une StringGrid avec les flèches droite et gauche parmi des colonnes dont le paramètre ColWidths=-1.
La valeur -1 permet d'avoir une colonne invisible.
Lorsqu'on se déplace avec les flèches, l'idée est de sauter les colonnes masquées. N'importe quelle colonne peut être masquée et il peut y avoir plusieurs colonnes masquées consécutives n'importe où (au début, au milieu, à la fin).
Dernière idée, si la première ou la dernière colonne est masquée, le focus doit rester sur la colonne la plus proche dont le paramètre ColWidths>0.
Pour résoudre le problème, il suffit de mettre le code dans la procédure KeyUp.
Code Delphi : | Sélectionner tout |
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 | procedure TForm1.StringGrid1KeyUp(Sender: TObject; var Key: Word; Shift: TShiftState); begin if Key=VK_LEFT then begin if StringGrid1.ColWidths[StringGrid1.Col]<=0 then if StringGrid1.Col-1>=0 then begin StringGrid1.Col:=StringGrid1.Col-1; end; end; if Key=VK_RIGHT then begin if StringGrid1.ColWidths[StringGrid1.Col] <=0 then if StringGrid1.Col+1<=StringGrid1.ColCount-1 then begin StringGrid1.Col:=StringGrid1.Col+1; end; end; if (StringGrid1.ColWidths[StringGrid1.Col]<=-1) and (StringGrid1.Col<StringGrid1.ColCount-1) and (StringGrid1.Col>0) then begin StringGrid1KeyUp(Sender,Key,Shift); Exit; end; if (StringGrid1.ColWidths[StringGrid1.Col]<=-1) then begin if (StringGrid1.Col=0) then begin Repeat StringGrid1.Col:=StringGrid1.Col+1 Until StringGrid1.ColWidths[StringGrid1.Col]>0; end; if (StringGrid1.Col=StringGrid1.ColCount-1) then begin Repeat StringGrid1.Col:=StringGrid1.Col-1 Until StringGrid1.ColWidths[StringGrid1.Col]>0; end; end; end; |
Code Delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 | type TForm1 = class(TForm) StringGrid1: TStringGrid; procedure StringGrid1SelectCell(Sender: TObject; ACol, ARow: Integer; var CanSelect: Boolean); procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean); procedure FormShow(Sender: TObject); private { Déclarations privées } MyComboBox: TComboBox; procedure MyComboBoxExit(Sender: TObject); end; |
Code Delphi : | Sélectionner tout |
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 | procedure TForm1.MyComboBoxExit(Sender: TObject); begin StringGrid1.Cells[StringGrid1.Col, StringGrid1.Row] := MyComboBox.Text; MyComboBox.Visible := False; StringGrid1.SetFocus; end; procedure TForm1.FormShow(Sender: TObject); begin StringGrid1.Options := StringGrid1.Options + [goColSizing, goThumbTracking] - [goEditing]; MyComboBox := TComboBox.Create(Self); with MyComboBox do begin Parent := Form1; Visible := False; OnExit := MyComboBoxExit; Items.Add('Delphi XE'); Items.Add('Delphi XE2'); Items.Add('Delphi XE3'); Items.Add('Delphi XE4'); Items.Add('Delphi XE5'); Items.Add('Delphi XE6'); Items.Add('Delphi XE7'); Items.Add('Delphi XE8'); Items.Add('Delphi 10 Seattle'); end; end; procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean); begin FreeAndNil(MyComboBox) end; procedure TForm1.StringGrid1SelectCell(Sender: TObject; ACol, ARow: Integer; var CanSelect: Boolean); var R: TRect; begin if (ACol > 0) and (ARow <> 0) then begin R := StringGrid1.CellRect(ACol, ARow); R.Left := R.Left + StringGrid1.Left; R.Right := R.Right + StringGrid1.Left; R.Top := R.Top + StringGrid1.Top; R.Bottom := R.Bottom + StringGrid1.Top; with MyComboBox do begin Left := R.Left + 1; Top := R.Top + 1; Width := (R.Right + 1) - R.Left; Height := (R.Bottom + 1) - R.Top; Visible := True; end; if StringGrid1.Cells[ACol, ARow] <> EmptyStr then MyComboBox.Text := StringGrid1.Cells[ACol, ARow] else MyComboBox.Text := EmptyStr; MyComboBox.SetFocus; end; CanSelect := True; end; |
Proposer une nouvelle réponse sur la FAQ
Ce n'est pas l'endroit pour poser des questions, allez plutôt sur le forum de la rubrique pour çaLes sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2024 Developpez Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.