FAQ C++ BuilderConsultez toutes les FAQ
Nombre d'auteurs : 60, nombre de questions : 670, dernière mise à jour : 21 novembre 2010 Ajouter une question
Cette F.A.Q. a été réalisée à partir des questions fréquemment posées sur le forum C++ Builder de developpez.com et de l'expérience personnelle des auteurs.
Nous tenons à souligner que cette F.A.Q. ne garantit en aucun cas que les informations qu'elle propose soient correctes. Les auteurs font le maximum, mais l'erreur est humaine. Cette F.A.Q. ne prétend pas non plus être complète. Si vous trouvez une erreur, ou que vous souhaitez devenir rédacteur, contactez pottiez
Nous espérons que cette F.A.Q. saura répondre à un maximum de vos questions. Nous vous souhaitons une bonne lecture.
L'équipe C++ Builder de Developpez.
Commentez cette FAQ : Commentez
- Comment personnaliser un ensemble de cellules sans modifier toute la grille dans une TStringGrid ?
- Comment écrire sur plusieurs lignes dans une cellule d'un TStringGrid?
- Comment gérer la roulette de la souris sur un TDBGrid ?
- Comment supprimer une ligne ou une colonne dans un TStringGrid ?
- Comment simuler une multiselection dans un StringGrid ?
- Comment empêcher l'édition d'une cellule spécifique d'un TStringGrid ?
- Comment déplacer une ligne ou une colonne dans une TStringGrid ?
- Comment avoir des retours chariot dans une cellule d'une StringGrid ?
- Comment mettre une image en fond d'un TStringGrid ?
- Comment centrer le texte d'un TStringGrid ?
- Comment écrire le texte d'une cellule d'un TStringGrid sur plusieurs ligne ?
Surchargeons la méthode OnDrawCell : cette méthode est appelée pour chaque cellule devant être (re)dessinée.
Attention : Les attributs Col, Row, Rect et State concernent donc la cellule en train d'être dessinée, et non la cellule courante (que vous sélectionnez par exemple avec votre souris).
Code c++ : | 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 | TColor CLTitre = clBtnFace, CLFond = clYellow, CLSelection = clRed; // Vos couleurs personnalisées. AnsiString s = StringGrid1->Cells[ACol][ARow]; // Texte de la cellule. if (ACol < StringGrid1->FixedCols || ARow < StringGrid1->FixedRows) { // Concerne les titres des lignes et des colonnes. StringGrid1->Canvas->Brush->Color = CLTitre; StringGrid1->Canvas->FillRect(Rect); // On dessine le fond de la cellule. StringGrid1->Canvas->Font->Style = StringGrid1->Canvas->Font->Style << fsBold; // On met les titres en gras. // On réaffiche le texte par dessus, car le FillRect l'a effacé. StringGrid1->Canvas->TextRect( Rect, (Rect.Right + Rect.Left - StringGrid1->Canvas->TextWidth(s)) / 2, (Rect.Top + Rect.Bottom - StringGrid1->Canvas->TextHeight(s)) / 2, s); // Le premier argument est le rectangle de rognage, les deux suivants permettent de centrer le texte dans la cellule. } else if (State.Contains(gdSelected)) { // Concerne la(les) cellule(s) sélectionnée(s). StringGrid1->Canvas->Brush->Color = CLSelection; StringGrid1->Canvas->FillRect(Rect); StringGrid1->Canvas->Font->Color = clWhite; // Le texte est écrit en blanc. StringGrid1->Canvas->TextRect(Rect, Rect.Left +1, Rect.Top + 1, s); // Ici texte justifié à gauche. } else { StringGrid1->Canvas->Brush->Color = CLFond; StringGrid1->Canvas->FillRect(Rect); StringGrid1->Canvas->TextRect(Rect, Rect.Left +1, Rect.Top + 1, s); } |
Le but est donc tout simplement d'afficher le texte de votre cellule sur plusieurs lignes, s'il est trop long par exemple, ou simplement pour améliorer la présentation de vos données.
Le principe :
- Utiliser un TMemo invisible de la largeur de la case, et le laisser découper votre texte (propriété WordWrap à true).
- Récupérer les n lignes du TMemo une par une, et les afficher dans des rectangles n fois plus petits (en hauteur).
N.B. Si vous ne maitrisez pas trop le OnDrawCell ou l'utilisation du TCanvas de la grille, je vous conseille de lire d'abord .
Code c++ : | 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 | #include <math.hpp> // A placer dans l'évènement OnDrawCell de votre TStringGrid. TMemo *Memo = new TMemo(this); Memo->Visible = false; Memo->Parent = this; Memo->Lines->Text = StringGrid1->Cells[ACol][ARow]; Memo->Width = StringGrid1->ColWidths[ACol]; int Cell_Height = Rect.Bottom - Rect.Top, Ligne_Nbr = Memo->Lines->Count; // Cette ligne permet d'éviter le chevauchement des lignes si le texte est vraiment trop long pour la case. Ligne_Nbr = Min(Ligne_Nbr, (Rect.Bottom - Rect.Top) / StringGrid1->Canvas->TextHeight(" ") + 1); for (int i = 0 ; i < Ligne_Nbr ; i++) { TRect R = Rect; AnsiString Text_i = Memo->Lines->Strings[i]; R.Top = Rect.Top + i * Cell_Height / Ligne_Nbr; R.Bottom = Rect.Top + (i + 1) * Cell_Height / Ligne_Nbr; // Texte justifié "centré" StringGrid1->Canvas->TextRect( R, (R.Right + R.Left - StringGrid1->Canvas->TextWidth(Text_i)) / 2, (R.Top + R.Bottom - StringGrid1->Canvas->TextHeight(Text_i)) / 2, Text_i); } delete Memo; |
La roulette n'est pas gérée correctement par défaut dans le composant TDBGrid. Pour qu'elle soit prise en compte il faut suivre la méthode suivante, elle est basée sur la dérivation de la méthode WindowProc de la grille afin de gérer le message WM_MOUSEWHEEL.
Dans le header de la fiche, ajouter :
Code c++ : | Sélectionner tout |
1 2 3 | private: // Déclarations de l'utilisateur TWndMethod OldWindowProc; void __fastcall DBGridNewWindowProc(TMessage &Msg); |
Code c++ : | Sélectionner tout |
1 2 | OldWindowProc = DBGrid1->WindowProc; DBGrid1->WindowProc = DBGridNewWindowProc; |
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | void __fastcall TForm1::DBGridNewWindowProc(TMessage &Msg) { if (Msg.Msg == WM_MOUSEWHEEL) { if (DBGrid1->DataSource->DataSet->Active) { if (short(Msg.WParamHi) < 0) { DBGrid1->DataSource->DataSet->Next(); } else { DBGrid1->DataSource->DataSet->Prior(); } } return; } OldWindowProc(Msg); } |
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 c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 | class TPublicStringGrid: public TCustomGrid { public: using TCustomGrid::DeleteRow; using TCustomGrid::DeleteColumn; }; // 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), la première étant la colonne n°0) : ((TPublicStringGrid*)StringGrid1)->DeleteColumn(1); |
Pour simuler la sélection multiple dans cet objet, il faudra intercepter des évènements clavier et ainsi il faut que la propriété KeyPreview de la TForm contenant la grille soit à true.
Ensuite, il faut travailler sur les évènements suivants :
Pour le TStringGrid
- OnClick
- OnDrawCell
- OnKeyDown
- OnKeyUp
- OnMouseMove
Pour la TForm
- OnCreate
- OnClose
Code c++ : | Sélectionner tout |
1 2 3 4 | Private: TStringGrid *pGrille; TStringList *pListeIndex; bool MultiSelect; |
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | void __fastcall TForm1::FormCreate(TObject *Sender) { MultiSelect = false; // On met la valeur de cette variable à 'faux' afin que le comportement // par défaut de la grille soit 'normal' et qu'il faille appuyer sur la touche // 'ctrl' pour simuler la sélection multiple } //----------------------------------------------------------------------- void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action) { // On libère la mémoire allouée à l'objet TStringList si il a été crée if(pListeIndex != NULL) delete pListeIndex; } |
Code c++ : | 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 | void __fastcall TForm1::StringGridKeyDown(TObject *Sender, WORD &Key, TShiftState Shift) { //Si la touche ctrl est enfoncée, on crée la liste pour stocker les index des lignes sélectionnées if (Key == VK_CONTROL) { MultiSelect = true; // on teste l'existance ou non d'un objet 'StringList', ainsi on ne redéclare // pas inutilement des objets de ce type à chaque fois que la touche est pressée if (pListeIndex == NULL) { pListeIndex = new TStringList(); // ainsi la liste est créée et elle sera persistante tant que le // bouton 'ctrl' est enfoncé. Cette liste va stocker les index des // lignes sélectionnées en cliquant dans la stringgrid } } } //--------------------------------------------------------------------------- void __fastcall TForm1::StringGridKeyUp(TObject *Sender, WORD &Key, TShiftState Shift) { // Si la touche ctrl est relachée, on vide la liste supposée contenir les index des lignes selectionnées if (Key == VK_CONTROL) { MultiSelect = false; if (!pListeIndex == NULL) { pListeIndex->Clear(); // la liste est vidée mais non détruite : l'objet de type 'TStringList' // sera libérée au moment du OnClose de la form } } } |
Code c++ : | 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 | void __fastcall TForm1::StringGridDrawCell(TObject *Sender, int ACol, int ARow, TRect &Rect, TGridDrawState State) { pGrille = (TStringGrid* )Sender; if (!pListeIndex == NULL) { // Si l'index de la ligne dessinée correspond à une ligne sélectionnée, on // retrouve la valeur de son index dans la TStringList if (pListeIndex->IndexOf(ARow)!= - 1) { //elle est déssinée avec une couleur de fond et une fonte spécifique pGrille->Canvas->Brush->Color = clHighlight; pGrille->Canvas->Font->Color = clWhite; pGrille->Canvas->FillRect(Rect); pGrille->Canvas->TextRect(Rect, Rect.Left+2, Rect.Top+2, pGrille->Cells[ACol][ARow]); } } else { pGrille->Canvas->Brush->Color = clWindow; pGrille->Canvas->Font->Color = clBlack; } } |
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | void __fastcall TForm1::StringGridClick(TObject *Sender) { pGrille = (TStringGrid* )Sender; // on teste la valeur de 'MultiSelect' pour savoir si la touche 'ctrl' a été // pressée if (MultiSelect == true) { // on enregistre dans la TstringList l'index de la ligne selectionnée pListeIndex->Add(pGrille->Row); } // on force la grille a se 'redessiner', afin de mettre a jour l'affichage // en tenant compte des nouvelles valeurs ou des valeurs non stockées dans la TStringList pGrille->Repaint(); } |
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 | void __fastcall TForm1::StringGridMouseMove(TObject *Sender, TShiftState Shift, int X, int Y) { pGrille = (TStringGrid* )Sender; pGrille->SetFocus(); } |
Pour empêcher l'édition des cellules du TStringGrid, il faut jouer sur la propriété option de celui-ci. En effet, dans les options du TStringGrid, on peut cocher l'option goEditing qui permet d'autoriser ou non l'édition du TStringGrid.
Il vous suffit alors de vérifier quelle est la cellule que l'utilisateur souhaite éditer, et autoriser ainsi cette édition ou non. Cela se fait grâce à l'évènement OnSelectCell().
Cela donne ce code (ici pour vérouiller la cellule de coordonné 1;1) :
Code c++ : | Sélectionner tout |
1 2 3 4 5 | if((ACol == 1) && (ARow == 1)){ StringGrid1->Options >> goEditing; // ne pas rendre éditable }else{ StringGrid1->Options << goEditing; // rendre éditable } |
Si vous voulez vérouiller plusieurs cellules, je vous propose d'utiliser un vecteur et de vérifier chaque valeur du vecteur grâce à un boucle
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 | int vectCoordonnee[5][2] = {{1,1},{2,5},{3,1},{1,4},{1,2}} ; int i ; StringGrid1->Options << goEditing; // rendre éditable for(i=0;i<5;i++){ if((ACol == vectCoordonnee[i][0]) && (ARow == vectCoordonnee[i][1])){ StringGrid1->Options >> goEditing; // ne pas rendre éditable } } |
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 c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 | void __fastcall TForm1::Button1Click(TObject *Sender) { class TPublicStringGrid: public TCustomGrid { public: using TCustomGrid::MoveRow; using TCustomGrid::MoveColumn; }; // 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); } |
Pour cela, il faut activer l'option goAlwaysShowEditor et ajouter le code suivant dans l'événement OnKeyDown :
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 | void __fastcall TForm1::StringGrid1KeyDown(TObject *Sender, WORD &Key, TShiftState Shift) { if(Key == VK_RETURN) { StringGrid1->Cells[StringGrid1->Col][StringGrid1->Row] = StringGrid1->Cells[StringGrid1->Col][StringGrid1->Row] + sLineBreak; } } |
Code c++ : | 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 | void __fastcall TForm1::StringGrid1DrawCell(TObject *Sender, int ACol, int ARow, TRect &Rect, TGridDrawState State) { // Sélection de la couleur de fond if(State.Contains(gdFixed)) { StringGrid1->Canvas->Brush->Color = clBtnFace; } else if(State.Contains(gdSelected)) { StringGrid1->Canvas->Brush->Color = clNavy; } else { StringGrid1->Canvas->Brush->Color = clWhite; } // Dessin du fond StringGrid1->Canvas->FillRect(Rect); // Sélection de la couleur de texte if(State.Contains(gdSelected)) { SetTextColor(Canvas->Handle, clWhite); } else { SetTextColor(Canvas->Handle, clBlack); } // Dessin du texte en utilisant la fonction API DrawText(StringGrid1->Canvas->Handle, (StringGrid1->Cells[ACol][ARow]).c_str(), -1, &Rect, DT_NOPREFIX | DT_WORDBREAK ); } |
Voici le code à appliquer pour mettre une image en fond des cellules blanches d'un TStringGrid. L'image est contenue dans un Bitmap, mais elle peut aussi être contenue 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 c++ : | 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 | void __fastcall TForm1::StringGrid1DrawCell(TObject *Sender, int ACol, int ARow, TRect &Rect, TGridDrawState State) { int i, j, X, Y; TRect R; if(State.Contains(gdFixed)) { // Les cellules fixes sont toujours dessinées en gris StringGrid1->Canvas->Brush->Color = clBtnFace; StringGrid1->Canvas->Brush->Style = bsSolid; StringGrid1->Canvas->FillRect(Rect); } else if(State.Contains(gdSelected)) { // Les cellules sélectionnées sont en bleu StringGrid1->Canvas->Brush->Color = clNavy; StringGrid1->Canvas->Brush->Style = bsSolid; StringGrid1->Canvas->FillRect(Rect); } else { // 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 = StringGrid1->FixedCols + 1; i <= ACol; i++) (X++, StringGrid1->ColWidths [i]); { Y = 0; for(i = StringGrid1->FixedRows + 1; i <= ARow; i++) (Y++, StringGrid1->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 Image1->Visible = false; Image1->Picture->Bitmap->LoadFromFile("C:\\Documents and Settings\\toto\\Mes documents\\Mes images\\Massiv10\\Massiv10\\Bitmaps\\Arrow\\arcarrow1.bmp"); // "C:\\Documents and Settings\\toto\\Mes documents\\Mes images\\Massiv10\\Massiv10\\Bitmaps\\Arrow\\arcarrow1.bmp" StringGrid1->Canvas->CopyRect(Rect, Image1->Picture->Bitmap->Canvas, R); StringGrid1->Canvas->Brush->Style = bsClear; } } } // Sélection de la couleur de texte if(State.Contains(gdSelected)) { SetTextColor(StringGrid1->Canvas->Handle, clWhite); } else { SetTextColor(StringGrid1->Canvas->Handle, clBlack); } // Dessin du texte en utilisant la fonction API DrawText(StringGrid1->Canvas->Handle, (StringGrid1->Cells[ACol][ARow]).c_str(), -1, &Rect, DT_NOPREFIX ); } |
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 c++ : | 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 | void __fastcall TForm1::StringGrid1DrawCell(TObject *Sender, int ACol, int ARow, TRect &Rect, TGridDrawState State) { // sélection de la couleur de fond if(State.Contains(gdFixed)) { StringGrid1->Canvas->Brush->Color = clBtnFace; } else if(State.Contains(gdSelected)) { StringGrid1->Canvas->Brush->Color = clNavy; } else { StringGrid1->Canvas->Brush->Color = clWhite; } // Dessin du fond StringGrid1->Canvas->FillRect(Rect); // Sélection de la couleur de texte if(State.Contains(gdSelected)) { StringGrid1->Font->Color = clWhite; } else { StringGrid1->Font->Color = clBlack; } // Dessin du texte en utilisant la fonction API DrawText(StringGrid1->Canvas->Handle, (StringGrid1->Cells[ACol][ARow]).c_str(), -1, &Rect, DT_CENTER | DT_NOPREFIX | DT_VCENTER | DT_SINGLELINE ); } |
Le code suivant permet d'écrire le texte d'une cellule d'un TStringGrid sur plusieurs lignes.
Code c++ : | 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 | void __fastcall TForm1::StringGrid1DrawCell(TObject *Sender, int ACol, int ARow, TRect &Rect, TGridDrawState State) { // sélection de la couleur de fond if(State.Contains(gdFixed)) { StringGrid1->Canvas->Brush->Color = clBtnFace; } else if(State.Contains(gdSelected)) { StringGrid1->Canvas->Brush->Color = clNavy; } else { StringGrid1->Canvas->Brush->Color = clWhite; } // Dessin du fond StringGrid1->Canvas->FillRect(Rect); // Sélection de la couleur de texte if(State.Contains(gdSelected)) { SetTextColor(StringGrid1->Canvas->Handle, clWhite); } else { SetTextColor(StringGrid1->Canvas->Handle, clBlack); } // Dessin du texte en utilisant la fonction API DrawText(StringGrid1->Canvas->Handle, (StringGrid1->Cells[ACol][ARow]).c_str(), -1, &Rect, DT_CENTER | DT_NOPREFIX | DT_WORDBREAK); } |
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.