C++ Builder Snippets  


TStringGrid: mehrere Zellen oder Zellenbereiche selektieren:
 
Die aus jedem Tabellenkalkulationsprogramm bekannte Möglichkeit mehrere Zellen oder Zellenbereiche einer Tabelle mit Hilfe der Strg-Taste zu selektieren wird von der VCL-Komponente TStringGrid nicht direkt unterstützt. Nachfolgend wird beschrieben, wie Sie TStringGrid um diese Funktionalität erweitern.

Screenshot des Beispielprojekts

Das Prinzip der Umsetzung dieser Funktionalität ist - trotz der auf den ersten Blick vielleicht etwas verwirrenden Implementierung recht einfach: sobald im OnDrawCell-Ereignis eine "selektierte" Zelle gezeichnet wird, werden ihre Koordinaten in einer TGridCoord-Struktur abgelegt und der Zeiger auf diese einer TList hinzugefügt. Alle Zellen, deren Koordinaten in dieser "Auswahl-Liste" vorhanden sind, werden in der Auswahl-Farbe gezeichnet.

Geleert wird die Liste jedesmal, wenn die Auswahl durch den Benutzer mit Hilfe der Tastatur oder Maus geändert wird und die Strg-Taste dabei nicht gedrückt ist.

Mit Hilfe der Funktionen GetSelectedCellsCount() und GetSelectedCoord(int ilIndex) kann die Anzahl der Zellen in der aktuellen Auswahl und die Koordinaten jeder Zelle ausgelesen werden.

Im Beispielprojekt wird eine TStringGrid-Komponente namens StringGrid1, eine TMemo Memo1 (für die Ausgabe der Auswahl-Liste) und ein TButton Button1 (Schaltfläche "Auswahl anzeigen") verwendet:

//----  Header-Datei FormMain.h zum Hauptformular der Anwendung: ------------
#ifndef FormMainH
#define FormMainH
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <Grids.hpp>

// Vergleichsfunktion zum Sortieren des Arrays mit den Koordinaten der
// selektierten Zellen (wird der Sort()-Methode der TList übergeben):
int __fastcall SelectionListSortCompare(void * Item1, void * Item2);

//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:  // Von der IDE verwaltete Komponenten
  TStringGrid *StringGrid1;
  TLabel *Label2;
  TMemo *Memo1;
     TButton *Button1;
  void __fastcall FormCreate(TObject *Sender);
  void __fastcall FormDestroy(TObject *Sender);
  void __fastcall StringGrid1DrawCell(TObject *Sender, int ACol, int ARow,
          TRect &Rect, TGridDrawState State);
  void __fastcall StringGrid1MouseDown(TObject *Sender, TMouseButton Button,
          TShiftState Shift, int X, int Y);
  void __fastcall StringGrid1MouseUp(TObject *Sender, TMouseButton Button,
          TShiftState Shift, int X, int Y);
  void __fastcall StringGrid1KeyDown(TObject *Sender, WORD &Key,
          TShiftState Shift);
  void __fastcall StringGrid1KeyUp(TObject *Sender, WORD &Key,
          TShiftState Shift);
     void __fastcall Button1Click(TObject *Sender);

private:  // Anwender-Deklarationen

  // Liste mit Zeigern auf die TGridCoord-Strukturen
  // zur Verwaltung der Zellenkoordinaten der Auswahl
  TList* pSelCells;

  // Flag "Auswahl entfernen" (wird zum Umschalten der "Zelle ausgewählt"-
  // Statusinformation beim Klick auf die ausgewählte Zelle verwendet)
  bool bgRemove;

  // Zellenkoordinaten in die Auswahl-Liste einfügen:
  void AddCellToList(int ilCol, int ilRow);

  // Prüfen, ob die überg. Zellenkoord. in der Liste bereits vorhanden sind
  bool CellInList(int ilCol, int ilRow);

  // Aktuelle Auswahl anzeigen:
  void ShowCurrentSelection(void);

  // Aktuelle Auswahl zurücksetzen:
  void ClearCurrentSelection(bool blRepaint = true);

  // Legt die Zellenkoordinaten der aktuellen Auswahl in der Liste ab:
  void CopySelectonToList(void);

  // Löscht die Zellenkoordinaten der übergebenen Zelle aus der Liste:
  void RemoveFromList(int ilCol, int ilRow);

  // Funktion GetSelectedCellsCount() liefert die Anzahl der selektierten Zellen
  int GetSelectedCellsCount(void);

  // Funktion GetSelectedCoord() liefert einen Zeiger auf die TGridCoord-
  // Struktur mit dem Zeilen- und Spaltenindex der ausgew. Zelle mit dem
  // überg. Auswahl-Index
  TGridCoord* GetSelectedCoord(int ilIndex);

  // Zeichen der Texte in die Tabellenzelle gem. der Ausrichtungsvorgabe:
  void __fastcall DrawCellText(Graphics::TCanvas* pCanvas,
                               AnsiString slStr, TRect &Rect,
                               TAlignment eAlignment);


public:    // Anwender-Deklarationen
  __fastcall TForm1(TComponent* Owner);
};

//-----  Quellcode-Datei FormMain.cpp zum Hauptformular der Anwendung: -----

#include <vcl.h> #pragma hdrstop #include "FormMain.h" //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; //--------------------------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } //--------------------------------------------------------------------------- // OnCreate-Eventhandler des Formulars //--------------------------------------------------------------------------- void __fastcall TForm1::FormCreate(TObject *Sender) { pSelCells = new TList; // Liste mit Zeigern auf die TGridCoord-Strukturen // zur Verwaltung der Zellenkoordinaten der Auswahl // (Auswahl-Liste) instanziieren bgRemove = false; // "Auswahl entfernen"-Flag // Zeilen-Überschriften eintragen: for(int ilRow = 1; ilRow < StringGrid1->RowCount; ilRow++) StringGrid1->Cells[0][ilRow] = "Zeile " + IntToStr(ilRow); // Spalten-Überschriften eintragen: for(int ilCol = 1; ilCol < StringGrid1->ColCount; ilCol++) StringGrid1->Cells[ilCol][0] = "Spalte " + IntToStr(ilCol); // Tabelle mit Beispiel-Werten füllen: for(int ilRow = 1; ilRow < StringGrid1->RowCount; ilRow++) for(int ilCol = 1; ilCol < StringGrid1->ColCount; ilCol++) StringGrid1->Cells[ilCol][ilRow] = IntToStr(ilCol) + ":" + IntToStr(ilRow); } //--------------------------------------------------------------------------- // OnDrawCell-Ereignisbehandlung der Tabelle //--------------------------------------------------------------------------- // Sobald StringGrid versucht, eine "selektierte" Zelle zu zeichnen, // werden ihre Koordinaten in unsere Auswahl-Liste eingefügt. Alle // Zellen, deren Koordintaten in der Auswahl-Liste zu finden sind werden // selektiert gezeichnet. //--------------------------------------------------------------------------- void __fastcall TForm1::StringGrid1DrawCell(TObject *Sender, int ACol, int ARow, TRect &Rect, TGridDrawState State) { TStringGrid* pGrid = dynamic_cast<TStringGrid*>(Sender); // falls eine selektierte Zelle gezeichnet werden soll, deren // Koordinaten in die Auswahl-Liste einfügen: if(State.Contains(gdSelected) || State.Contains(gdFocused)) { if(!bgRemove) // Falls die Zellenauswahl nicht "abgewählt" AddCellToList(ACol, ARow); // Zellenkoord. in die Auswahlliste einfügen else // Falls die Zellenauswahl abgewählt: { // falls zeilenweise Auswahl aktviert (Option goRowSelect aktiv): if(pGrid->Options.Contains(goRowSelect)) { // alle Zellen der Zeil aus der Auswahl-Liste entfernen: for(int ilCol = 0; ilCol < pGrid->ColCount; ilCol++) RemoveFromList(ilCol, ARow); } else RemoveFromList(ACol, ARow); // Zelle aus der Auswahl-Liste entfernen pGrid->Canvas->Font->Color = pGrid->Font->Color; } } // falls die Koordinaten der zu zeichnenden Zelle sich // in der Auswahl-Liste befinden: if(CellInList(ACol, ARow)) { // Zelle in der Auswahl-Farbe zeichnen: pGrid->Canvas->Brush->Color = clHighlight; pGrid->Canvas->Font->Color = pGrid->Color; } else if(!State.Contains(gdFixed)) // andernfalls: pGrid->Canvas->Brush->Color = pGrid->Color; // die Zellenfarbe verwenden // Text in die Zelle ausgeben: DrawCellText(pGrid->Canvas, pGrid->Cells[ACol][ARow], Rect, taLeftJustify); } //--------------------------------------------------------------------------- // Eventhandler für das OnMouseDown-Ereignis der Tabelle //--------------------------------------------------------------------------- void __fastcall TForm1::StringGrid1MouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { // Sender-Zeiger casten: TStringGrid* pGrid = dynamic_cast<TStringGrid*>(Sender); // "Koordinaten" der angeklickten Zelle bestimmen: int ilCol, ilRow; pGrid->MouseToCell(X,Y,ilCol, ilRow); // falls die Tabelle mit der linken Maustaste angeklickt // wurde und die Strg-Taste gedrückt ist: if(ilCol > 0 && ilRow > 0 && Button == mbLeft && Shift.Contains(ssCtrl)) { // falls die Zelle bereits selektirt (in der Auswahl-Liste) ist, abwählen: if(CellInList(ilCol, ilRow)) bgRemove = true; else // falls eine nicht selektirte Zelle... { // Markierung in der Tabelle in die Auswahl-Liste übernehmen: CopySelectonToList(); bgRemove = false; // Flag "Auswahl entfernen" zurücksetzen } } // falls die Tabelle mit der linken Maustaste angeklickt // wurde und die Strg-Taste NICHT gedrückt ist: else if(ilCol > 0 && ilRow > 0 && Button == mbLeft && !Shift.Contains(ssCtrl)) { ClearCurrentSelection(true); // Auswahl-Liste leeren bgRemove = false; // Flag "Auswahl entfernen" zurücksetzen } } //--------------------------------------------------------------------------- // Eventhandler für das OnMouseUp-Ereignis der Tabelle //--------------------------------------------------------------------------- void __fastcall TForm1::StringGrid1MouseUp(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { // falls die linke Mousetaste losgelassen // wurde und die Strg-Taste NICHT gedrückt ist: if(Button == mbLeft && !Shift.Contains(ssCtrl)) // Markierung in der Tabelle in die Auswahl-Liste übernehmen: CopySelectonToList(); } //--------------------------------------------------------------------------- // Eventhandler für das OnKeyDown-Ereignis der Tabelle //--------------------------------------------------------------------------- void __fastcall TForm1::StringGrid1KeyDown(TObject *Sender, WORD &Key, TShiftState Shift) { // falls eine der Pfeiltasten betätigt wurde if(Key == VK_UP || Key == VK_DOWN || Key == VK_LEFT || Key == VK_RIGHT) // Auswahlliste leeren, falls Shift-Taste // nicht gedrückt war, Zellen neu zeichnen: ClearCurrentSelection(!Shift.Contains(ssShift)); } //--------------------------------------------------------------------------- // Eventhandler für das OnKeyUp-Ereignis der Tabelle //--------------------------------------------------------------------------- void __fastcall TForm1::StringGrid1KeyUp(TObject *Sender, WORD &Key, TShiftState Shift) { // falls eine der Pfeiltasten betätigt wurde if((Key == VK_UP || Key == VK_DOWN || Key == VK_LEFT || Key == VK_RIGHT)) // Markierung in der Tabelle in die Auswahl-Liste übernehmen: CopySelectonToList(); } //--------------------------------------------------------------------------- // Eventhandler für das OnClick-Ereignis der Schaltfläche "Auswahl anzeigen" //--------------------------------------------------------------------------- void __fastcall TForm1::Button1Click(TObject *Sender) { ShowCurrentSelection(); } //--------------------------------------------------------------------------- // Koordinaten der übergebenen Zelle in die Auswahl-Liste einfügen: //--------------------------------------------------------------------------- void TForm1::AddCellToList(int ilCol, int ilRow) { if(!CellInList(ilCol, ilRow)) // falls die Zelle noch // nicht in der Auswahl-Liste { TGridCoord* pTmpCoord = new TGridCoord; pTmpCoord->X = ilCol; pTmpCoord->Y = ilRow; pSelCells->Add(pTmpCoord); } } //--------------------------------------------------------------------------- // Prüfen, ob die Zelle mit den übergebenen Koordinaten in der // Auswahl-Liste bereits vorhanden ist: //--------------------------------------------------------------------------- bool TForm1::CellInList(int ilCol, int ilRow) { // für alle Einträge in der Auswahl-Liste: for(int ilCellPos = 0; ilCellPos < pSelCells->Count; ilCellPos++) // falls die Spaltenangabe mit der überg. Spalte übereinstimmt: if(((TGridCoord*)pSelCells->Items[ilCellPos])->X == ilCol) // falls die Zeilenangabe mit der überg. Zeile übereinstimmt: if(((TGridCoord*)pSelCells->Items[ilCellPos])->Y == ilRow) return true; return false; } //--------------------------------------------------------------------------- // Zelle mit den übergebenen Koordinaten aus der Auswahl-Liste löschen: //--------------------------------------------------------------------------- void TForm1::RemoveFromList(int ilCol, int ilRow) { for(int ilCellPos = 0; ilCellPos < pSelCells->Count; ilCellPos++) if(((TGridCoord*)pSelCells->Items[ilCellPos])->X == ilCol) if(((TGridCoord*)pSelCells->Items[ilCellPos])->Y == ilRow) { delete (TGridCoord*)pSelCells->Items[ilCellPos]; pSelCells->Delete(ilCellPos); return; } } //--------------------------------------------------------------------------- // OnDestroy-Eventhandler des Formulars //--------------------------------------------------------------------------- void __fastcall TForm1::FormDestroy(TObject *Sender) { // Speicher aufräumen: if(pSelCells) { // alle TGridCoord-Strukturen freigeben: for(int ilCellPos = 0; ilCellPos < pSelCells->Count; ilCellPos++) if(pSelCells->Items[ilCellPos]) delete (TGridCoord*)pSelCells->Items[ilCellPos]; pSelCells->Clear(); // Zeigerliste leeren delete pSelCells; // Zeigerliste freigeben } } //--------------------------------------------------------------------------- // Anzeigen der aktuellen Auswahl im Memo1-Feld: //--------------------------------------------------------------------------- void TForm1::ShowCurrentSelection(void) { Memo1->Lines->Clear(); // Einträge löschen pSelCells->Sort(SelectionListSortCompare); // Auswahl-Liste sortieren // Koordinaten der selektierten Zellen ausgeben: for(int ilCellPos = 0; ilCellPos < GetSelectedCellsCount(); ilCellPos++) if(GetSelectedCoord(ilCellPos) != NULL) Memo1->Lines->Add("Zeile: " + IntToStr(GetSelectedCoord(ilCellPos)->Y) + ", Spalte: " + IntToStr(GetSelectedCoord(ilCellPos)->X)); } //--------------------------------------------------------------------------- // Funktion GetSelectedCellsCount() liefert die Anzahl der selektierten Zellen //--------------------------------------------------------------------------- int TForm1::GetSelectedCellsCount(void) { return pSelCells ? pSelCells->Count : 0; } //--------------------------------------------------------------------------- // Funktion GetSelectedCoord() liefert einen Zeiger auf die TGridCoord- // Struktur mit dem Zeilen- und Spaltenindex der ausgew. Zelle mit dem // überg. Auswahl-Index //--------------------------------------------------------------------------- TGridCoord* TForm1::GetSelectedCoord(int ilIndex) { if(ilIndex >= 0 && ilIndex <= pSelCells->Count) return (TGridCoord*)pSelCells->Items[ilIndex]; else return NULL; } //--------------------------------------------------------------------------- // Vergleichsfunktion zum Sortieren des Arrays mit // den Koordinaten der selektierten Zellen (wird der Sort()-Methode // der TList übergeben): //--------------------------------------------------------------------------- int __fastcall SelectionListSortCompare(void * Item1, void * Item2) { // nach Zeilen sortieren int ilRetVal = ((TGridCoord*)Item1)->Y - ((TGridCoord*)Item2)->Y; // bei gleicher Zeile nach Spalten sortieren: if(ilRetVal == 0) ilRetVal = ((TGridCoord*)Item1)->X - ((TGridCoord*)Item2)->X; return ilRetVal; } //--------------------------------------------------------------------------- // Aktuelle Auswahl aus der Auswahlliste und (falls // blRepaint == true) aus der Tabelle löschen: //--------------------------------------------------------------------------- void TForm1::ClearCurrentSelection(bool blRepaint) { for(int ilCellPos = 0; ilCellPos < pSelCells->Count; ilCellPos++) { if(pSelCells->Items[ilCellPos]) { if(blRepaint) { InvalidateRect(StringGrid1->Handle, &StringGrid1->CellRect( ((TGridCoord*)pSelCells->Items[ilCellPos])->X, ((TGridCoord*)pSelCells->Items[ilCellPos])->Y), true); } delete (TGridCoord*)pSelCells->Items[ilCellPos]; } } pSelCells->Clear(); } //--------------------------------------------------------------------------- // Legt die Zellenkoordinaten der aktuellen Auswahl in der Auswahl-Liste ab: //--------------------------------------------------------------------------- void TForm1::CopySelectonToList(void) { for(int ilRow = StringGrid1->Selection.Top; ilRow <= StringGrid1->Selection.Bottom; ilRow++) for(int ilCol = StringGrid1->Selection.Left; ilCol <= StringGrid1->Selection.Right; ilCol++) AddCellToList(ilCol, ilRow); } //--------------------------------------------------------------------------- // Zeichnen der Texte in die Tabellenzelle gem. der Ausrichtungsvorgabe: //--------------------------------------------------------------------------- // Funktion benötigt folgende Parameter: // 1. Zeiger auf Canvas des TStringGrid -> Graphics::TCanvas* pCanvas // 2. Der Ausgabetext -> AnsiString slStr // 3. Rechteck mit der Zellenposition -> TRect &Rect // 4. Textausrichtung in der Zelle -> TAlignment eAlignment // (taLeftJustify, taRightJustify oder taCenter) //--------------------------------------------------------------------------- void __fastcall TForm1::DrawCellText(Graphics::TCanvas* pCanvas, AnsiString slStr, TRect &Rect, TAlignment eAlignment) { if(eAlignment == taLeftJustify) { pCanvas->TextRect(Rect, Rect.Left + 2, (Rect.Top+Rect.Bottom-pCanvas->TextHeight(slStr))/2, slStr); } else if(eAlignment == taRightJustify) { pCanvas->TextRect(Rect, Rect.Right - pCanvas->TextWidth(slStr) - 2, (Rect.Top+Rect.Bottom-pCanvas->TextHeight(slStr))/2, slStr); } else { pCanvas->TextRect(Rect, Rect.Left + (Rect.Right - Rect.Left)/2 - pCanvas->TextWidth(slStr)/2, (Rect.Top+Rect.Bottom- pCanvas->TextHeight(slStr))/2, slStr); } } //---------------------------------------------------------------------------

  Download BCB5
Projekt-Quellcode
Download
Demo-Exe

© '99-2002 by S. Kreutzmann