In einem StringGrid auf Scrollereignis reagieren und anderes StringGrid per Code scollen

// Ereignis TopLeftChanged von StringGrid1 nutzen
void __fastcall TForm1::StringGrid1TopLeftChanged(TObject *Sender)
{
     // horizontale ScrollBarposition von StringGrid1 ermitteln
     int iScrollPosH = GetScrollPos(StringGrid1->Handle, SB_HORZ);
     // horizontale ScrollBarposition von StringGrid2 setzen
     SetScrollPos(StringGrid2->Handle, SB_HORZ, iScrollPosH, true);
     // Anzeige des Inhalts von StringGrid2 an die akt. ScrollBarposition anpassen
     StringGrid2->Perform(WM_HSCROLL, MAKEWPARAM(SB_THUMBPOSITION, iScrollPosH), 0);

     // vertikale ScrollBarposition von StringGrid1 ermitteln
     int iScrollPosV = GetScrollPos(StringGrid1->Handle, SB_VERT);
     // vertikale ScrollBarposition von StringGrid2 setzen
     SetScrollPos(StringGrid2->Handle, SB_VERT, iScrollPosV, true);
     // Anzeige des Inhalts von StringGrid2 an die akt. ScrollBarposition anpassen
     StringGrid2->Perform(WM_VSCROLL, MAKEWPARAM(SB_THUMBPOSITION, iScrollPosV), 0);
}

Formatierte, gedrehte, mehrzeilige Textausgabe

void __fastcall StringGridRotateTextOut(TStringGrid *Grid, int ARow, int ACol, TRect Rect, AnsiString sOutput, AnsiString sFontName, TColor clFontColor, int iFontSize, int iRotation)
{
     Grid->Canvas->Font->Name = sFontName;
     Grid->Canvas->Font->Size = iFontSize;
     Grid->Canvas->Font->Color = clFontColor;

     // neue Schrift für die gedrehte Ausgabe zusammenbauen
     TFont *tf = new TFont();
     TLogFont lf;
     tf->Assign(Grid->Canvas->Font);
     GetObject(tf->Handle, sizeof(lf), &lf);
     lf.lfEscapement = iRotation * 10;
     lf.lfOrientation = 0;
     HFONT hfont = CreateFontIndirect(&lf);
     tf->Handle = hfont;
     Grid->Canvas->Font->Assign(tf);
     delete tf;

     // Zelle malen
     Grid->Canvas->FillRect(Rect);
     // Text ausgeben
     // Beachten: abhängig vom Drehwinkel muss die X- und Y-Koordinate richtig gesetzt werden!
     // bei 90° z.B. Rect.Left + 2, Rect.Bottom - 2
     // DrawText bietet u.A. die Möglichkeit Text mehrzeilig (DT_WORDBREAK) auszugeben
     DrawText(Grid->Canvas->Handle, sOutput.c_str(), -1, &Rect, DT_VCENTER | DT_CENTER | DT_WORDBREAK);

     // Textausgabe könnte anstatt mit DrawText auch mit TextRect erfolgen,
     // es ist hierbei aber keine mehrzeilige Ausgabe möglich
     // Grid->Canvas->TextRect(Rect, Rect.Left + 2, Rect.Top + 2, sOutput);
}

void __fastcall TForm1::StringGrid1DrawCell(TObject *Sender, int ACol, int ARow, TRect &Rect, TGridDrawState State)
{
     TStringGrid *Grid = (TStringGrid *)Sender;
     Grid->Canvas->Font->Color = clBlack;

     if (ACol == 0)
     {
        // 1. Spalte "normal" fett und schwarz zeichnen
        Grid->Canvas->Font->Style = TFontStyles() << fsBold;
        Grid->Canvas->FillRect(Rect);
        DrawText(Grid->Canvas->Handle, Grid->Cells[ACol][ARow].c_str(), -1, &Rect, DT_SINGLELINE | DT_VCENTER | DT_CENTER);
     }
     else
     {
         // alle anderen Spalten gedreht und verkleinert zeichnen
         Grid->Canvas->Font->Style = TFontStyles() >> fsBold;
         AnsiString sOutput = Grid->Cells[ACol][ARow] + "\n" + "noch eine Zeile :)";
         StringGridRotateTextOut(Grid, ARow, ACol, Rect, sOutput , "Arial", clBlack, 6, -10);
     }
}

Stringgrid schnell löschen

void ClearStringGrid(TStringGrid * SGrid)
{
     // Neuzeichnen des Grids verhindern -> schneller löschen
     SendMessage(SGrid->Handle, WM_SETREDRAW, false, 0);

     // spaltenweises Löschen
     for (int i = 0; i < SGrid->ColCount; i++) SGrid->Cols[i]->Clear();

     SGrid->RowCount = 2;

     // Neuzeichnen des Grids wieder ermöglichen
     SendMessage(SGrid->Handle, WM_SETREDRAW, true, 0);
     SGrid->Invalidate();
}

Bitmaps in Stringridzellen malen

// Vorraussetzung: TImagelist mit geladenen Bilderchen als Resource
void __fastcall TForm1::StringGrid1DrawCell(TObject *Sender, long Col, long Row, TRect &Rect, TGridDrawState State)
{
    TStringGrid *Grid = (TStringGrid *)Sender;

    if ((ARow != 0) && (ACol != 0))
    {
       // leeres Bitmapobjekt erzeugen
       Graphics::TBitmap *pBitMap = new Graphics::TBitmap;

       // Bitmap mit Index 0 aus einer Imagelist herauskopieren
       ilFunctionButtons->GetBitmap(0, pBitMap);

       if (pBitMap)
       {
          // Bitmap in den Rect-Bereich der akt. Zelle malen
          Grid->Canvas->Draw(Rect.Left, Rect.Top, pBitMap);

          // und kopiertes Bitmap löschen
          delete pBitMap;
       }
    }
}

WordBreak in Stringridzellen

__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) 
{
    StringGrid1->DefaultDrawing = false;
}

void __fastcall TForm1::StringGrid1DrawCell(TObject *Sender, long Col, long Row, TRect &Rect, TGridDrawState State)
{
    if (State.Contains(gdFixed))
    {
       StringGrid1->Canvas->Brush->Color = clBtnFace;
       StringGrid1->Canvas->Font->Color = clWindowText;
       StringGrid1->Canvas->FillRect(Rect);
       Frame3D(StringGrid1->Canvas, Rect, clBtnHighlight, clBtnShadow, 1);
    }
    else if (State.Contains(gdSelected))
         {
            StringGrid1->Canvas->Brush->Color = clHighlight;
            StringGrid1->Canvas->Font->Color = clHighlightText;
            StringGrid1->Canvas->FillRect(Rect);
         }
         else
         {
             StringGrid1->Canvas->Brush->Color = StringGrid1->Color;
             StringGrid1->Canvas->Font->Color = StringGrid1->Font->Color;
             StringGrid1->Canvas->FillRect(Rect);
         }

    StringGrid1->Canvas->FillRect(Rect); // Hintergrund übermalen

    UINT format = DT_LEFT | DT_WORDBREAK;
    AnsiString text = StringGrid1->Cells[Col][Row];

    DrawText(StringGrid1->Canvas->Handle, text.c_str(), text.Length(), &Rect, format);
}

Eine angewählte Zeile farbig hervorheben (focused row)

  • Normalerweise kann einfach im Objektinspektor unter Options->goRowSelect festgelegt werden, ob die ganze akt. gewählte Zeile in einem StringGrid markiert werden soll. Das Problem dabei ist, dass der User später nicht mehr in den einzelnen Zellen herumeditieren kann. Folgender Code umgeht dieses Problem:
  • *.h
  • private: // Anwender-Deklarationen
    
        int FocusedRow; //akt. gewählte Zeile (Row) abspeichern
    
        void __fastcall SetColWidth(TStringGrid *Grid); // Funktion zum Setzen der Spaltenbreite
    
  • *.cpp
  • void __fastcall TForm1::StringGrid1DrawCell(TObject *Sender, int ACol, int ARow, TRect &Rect, TGridDrawState State)
    {
        TStringGrid *Grid = (TStringGrid*)Sender;
    
        // Hilfsfunktion zum Einstellen der optimalen Breite der letzten Spalte
        if (ACol == Grid->ColCount - 1) SetColWidth(Grid);
    
        if (!Grid->Enabled) return;
    
        // wenn die Funktion innerhalb des editierbaren Zellbereiches ist,
        if (ACol ->= Grid->FixedCols && ARow ->= Grid->FixedRows)
        {
            // wenn die akt. gewählte Zeile erreicht wurde
            if (FocusedRow == ARow)
            {
                // blauer Markierungsbalken mit weißer Schrift
                Grid->Canvas->Brush->Color = clHotLight;
                Grid->Canvas->Font->Color = clWhite;
            }
            else
            {
                // sonst nur weißer Hintergrund mit schwarzer Schrift
                Grid->Canvas->Brush->Color = clWhite;
                Grid->Canvas->Font->Color = clBlack;
            }
        }
        else
        {
            // wenn nicht innerhalb des Editorbereiches, dann Titelleiste oder 1. Spalte fett malen
            Grid->Canvas->Font->Style = TFontStyles() << fsBold;
            Grid->Canvas->Font->Color = clBlack;
        }
    
        Grid->Canvas->FillRect(Rect); // Hintergrund malen
    
        // Text horizontal und vertikal zentriert in der Zelle ausgeben
        DrawText(Grid->Canvas->Handle, Grid->Cells[ACol][ARow].c_str(), -1, &Rect, DT_SINGLELINE | DT_VCENTER | DT_CENTER);
    }
    
    void __fastcall TForm1::StringGrid1SelectCell(TObject *Sender, int ACol, int ARow, bool &CanSelect)
    {
        TStringGrid *Grid = (TStringGrid*)Sender;
    
        if (CanSelect)
        {
            // Sobald eine Zelle angewählt wurde, die akt. Zeile speichern
            FocusedRow = ARow;
            Grid->Repaint(); // neu malen ist wichtig!
        }
    }
    
    void __fastcall TForm1::SetColWidth(TStringGrid *Grid)
    {
        int iCol;
        int iColWidthsSum = 0;
    
        for (iCol = 0; iCol < Grid->ColCount-1; iCol++)
        {
            iColWidthsSum += Grid->ColWidths[iCol] + Grid->GridLineWidth;
        }
    
        if (iColWidthsSum < Grid->ClientWidth)
        {
            Grid->ColWidths[Grid->ColCount-1] = Grid->ClientWidth - iColWidthsSum - 1;
        }
    }
    

StringGrid mit Quicksort sortieren

// Aufruf:
// QuickSortColumn(StringGrid1, Col, StringGrid1->FixedRows, StringGrid1->RowCount - 1, CheckBox1->Checked);
void __fastcall TForm1::QuickSortColumn(TStringGrid *grid, int Column, int links, int rechts, bool Desc)
{
    // rechte und linke grenze festlegen
    int i = links;
    int j = rechts;

    // den mittelwert bestimmen (sortierung aufteilen)
    int m = StrToInt(grid->Cells[Column][(int) ((links + rechts) / 2)]);

    do
    {
        // sortier-reihenfolge festlegen
        while (Desc ? (StrToInt(grid->Cells[Column][i]) < m) : (StrToInt(grid->Cells[Column][i]) > m)) i++;
        while (Desc ? (m < StrToInt(grid->Cells[Column][j])) : (m > StrToInt(grid->Cells[Column][j]))) j--;

        if (i <= j)
        {
            // Zeilen tauschen
            AnsiString tmp = grid->Rows[i]->CommaText;
            grid->Rows[i]->CommaText = grid->Rows[j]->CommaText;
            grid->Rows[j]->CommaText = tmp;

            i++;
            j--;
        }
    }
    while (i < j);

    // rekursiver aufruf
    if (links < j) QuickSortColumn(grid, Column, links, j, Desc);
    if (i < rechts) QuickSortColumn(grid, Column, i, rechts, Desc);
}

StringGrid Spalten hinzufügen/löschen

  • Mehrere Zeilen/Spalten in einem StringGrid auf einmal löschen/hinzufügen
  • Gelöscht wird von der selektierten Spalte/Zeile ausgehend nach rechts bzw. unten, eingefügt wird links vor bzw. über der selektierten Spalte/Zeile
// Grid - Zeiger auf das zu bearbeitende Grid
// bool Delete - true = Löschen, false = Hinzufügen
// bool Cols - true = Spalten, false = Zeilen
// int AdjCount - Zahl der zu löschenden/hinzuzufügenden Spalten/Zeilen

void AdjustGrid(TStringGrid *Grid, bool Delete, bool Cols, int AdjCount)
{
    if (Cols) /////////// Spalten bearbeiten /////////////////////////////////////
    {
        TStringList *list = new TStringList(); // Zwischenspeicher für Zeilen/Spalten

        if (Delete) // Spalte soll gelöscht werden
        {
            // wenn selektierte Spalte plus "Zahl zu löschender Spalten" grösser/
            // gleich Gesamtzahl der Spalten, entsprechend weniger Spalten löschen
            if (Grid->Col + AdjCount >= Grid->ColCount + 1) AdjCount = Grid->ColCount - Grid->Col;

            if (Grid->ColCount <= Grid->FixedCols + 1)
               for (int y = 0; y < Grid->RowCount; y++)
                   Grid->Cells[Grid->FixedCols][y] = "";
            else
            {
                // nicht über den Rand links hinauslöschen
                if (AdjCount > Grid->ColCount - (Grid->FixedCols + 1))
                   AdjCount = Grid->ColCount - (Grid->FixedCols + 1);

                // StringList mit den Einträgen der aktuellen Zeile füllen und
                // darin an der der Spalte entsprechenden Position die nötige
                // Zahl von Einträgen löschen; dann in die Zeile zurück kopieren
                for (int x = 0; x < Grid->RowCount; x++)
                {
                    list->Assign(Grid->Rows[x]);
                    for (int z = 0; z < AdjCount; z++) list->Delete(Grid->Col);

                    Grid->Rows[x]->Assign(list);
                }

                // die letzte(n) Spalte(n) entfernen
                Grid->ColCount -= AdjCount;
            }
        }
        else // Spalte soll eingefügt werden
        {
            Grid->ColCount += AdjCount; // neue Spalte(n) hinzufügen;

            for (int x = 0; x < Grid->RowCount; x++) // StringList mit den Ein-
            {
                // trägen der aktuellen
                list->Assign(Grid->Rows[x]); // Zeile füllen und darin
                for (int z = 0; z < AdjCount; z++) // an der der Spalte ent-
                    list->Insert(Grid->Col, ""); // sprechenden Stelle die
                Grid->Rows[x]->Assign(list); // nötige Anzahl von leeren Einträgen hinzufügen,
            }
        } // dann zurück kopieren

        delete list;
    }
    else // Zeilen bearbeiten
    {
        if (Delete) // Zeile soll gelöscht werden
        {
            // wenn selektierte Zeile plus "Zahl zu löschender Zeilen" grösser/
            // gleich Gesamtzahl der Zeilen, entsprechend weniger Zeilen löschen
            if (Grid->Row + AdjCount >= Grid->RowCount + 1) AdjCount = Grid->RowCount - Grid->Row;

            if (Grid->RowCount <= Grid->FixedRows + 1) // wenn nur eine Zeile, dann
               Grid->Rows[Grid->FixedRows]->Clear(); // einfach den Inhalt löschen
            else
            {
                int Zeile = Grid->Row - 1; // Zeileninhalt jeweils mit
                while (Zeile++ < Grid->RowCount) // dem der nächsten nicht zu
                      Grid->Rows[Zeile] = Grid->Rows[Zeile + AdjCount]; // löschenden Zeile ersetzen;

                Grid->RowCount -= AdjCount; // letzte Zeile(n) löschen
            }
        }
        else // Zeile soll eingefügt werden
        {
            Grid->RowCount += AdjCount; // neue Zeile(n) anfügen
            int Zeile = Grid->RowCount; // Zeileninhalt jeweils mit

            while (Zeile-- > Grid->Row && Zeile - AdjCount > 0)//dem der entsprechenden
                  Grid->Rows[Zeile] = Grid->Rows[Zeile - AdjCount];

            // vorhergehenden ersetzen;
            for (int z = 0; z < AdjCount; z++) // zum Schluss noch die
                Grid->Rows[Grid->Row + z]->Clear(); // "eingefügte(n)" Zeile(n) leeren
        }
    }
}

StringGrid Zellen einfärben/Text ausrichten

void __fastcall TForm1::StringGrid1DrawCell(TObject *Sender, int ACol, int ARow, TRect &amp;Rect, TGridDrawState State)
{
     TStringGrid *Grid = (TStringGrid *)Sender;
     int hAlign = DT_CENTER;

     Grid->Canvas->Font->Color = clBlack;

     switch (ACol)
     {
            case 0 : { if (ARow != 0)
                       {
                           hAlign = DT_RIGHT;
                       }

                       Grid->Canvas->Font->Style = TFontStyles() << fsBold;

                       break; }
            case 1 : { if (ARow == 0)
                       {
                          Grid->Canvas->Font->Style = TFontStyles() << fsBold;
                       }
                       else
                       {
                           hAlign = DT_LEFT;
                       }

                       break; }
     }

     if (State.Contains(gdFocused)) // wenn Zelle focusiert
     {
         Grid->Canvas->Brush->Color = clYellow; // gelbe Zelle
     }

     Grid->Canvas->FillRect(Rect); // Hintergrund übermalen

     DrawText(Grid->Canvas->Handle, Grid->Cells[ACol][ARow].c_str(), -1, &amp;Rect, DT_SINGLELINE | DT_VCENTER | hAlign);
}