C++ Builder Snippets  


TComboBox als Inplace-Editor im TStringGrid verwenden, TButton (z.B. für den Aufruf von Dialogen) in den Zellen anzeigen:
 
Der Inplace-Editor des TStringGrid erlaubt bekanntlich nur die Eingabe von Texten oder Zahlen über die Tastatur. Manchmal müssen aber die Eingabemöglichkeiten auf eine bestimmte Auswahl beschränkt werden, oder der Anwender die Möglichkeit haben, einen externen "Editor" (z.B. TMonthCalender oder TOpenDialog) aufzurufen. Nachfolgend wird beschrieben, wie Sie TStringGrid um diese Funktionalität erweitern.

Screenshot des Beispielprojekts

Im Beispielprojekt wird eine TComboBox und ein TButton im OnCreate-Eventhandler des Formulars erzeugt, aber nich nicht angezeigt (man könnte diese Steuerelemente natürlich auch zur Entwurfszeit auf dem Formular ablegen und ihre Visible-Eigenschaft auf false setzen.

Sobald TStringGrid ein OnClick-Ereignis "feuert" (es is z.B. auch beim Verschieben der Markierung mit Hilfe der Pfeiltasten der Fall), wird in der OnClick-Ereignibehandlung der Tabelle ausgewertet, in welcher Spalte sich die Auswahl befindet und ggf. die ComboBox oder die Ellipse-Schaltfläche angezeigt. Vor dem Einblenden der Steuerelemente werden diese mit Hilfe der Funktionen PlaceComboEditor() bzw. PlaceEllipseBtn() in der selektierten Zelle positioniert.

//----  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>
#include <ComCtrls.hpp>

// Konvertiert Datumsstring ("dd.mm.yyyy") zum TDateTime-Object
TDateTime ConvertToDateTime(AnsiString slString);

//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:  // Von der IDE verwaltete Komponenten
  TStringGrid *StringGrid1;
  void __fastcall StringGrid1Click(TObject *Sender);
  void __fastcall StringGrid1TopLeftChanged(TObject *Sender);
  void __fastcall FormCreate(TObject *Sender);
  void __fastcall FormResize(TObject *Sender);

private:  // Anwender-Deklarationen

  // Zeiger auf die TComboBox für die Bearbeitung der Zelleninhalten
  TComboBox* pComboEditor;
  // Zeiger auf TButton für den Aufruf von Monatskalender
  TButton*    pEllipseBtn;
  // Zeiger auf die Monatskalender-Instanz
  TMonthCalendar* pMonthCalendar;

  // Positionierung der ComboBox in der ausgewählten Zelle
  void __fastcall PlaceComboEditor(void);
  // Positionierung der "..."-Schaltfläche in der ausgewählten Zelle
  void __fastcall PlaceEllipseBtn(void);
  // Eventhandler für die Änderung der Auswahl in der Inplace-ComboBox
  void __fastcall ComboEditorChange(TObject *Sender);
  // Eventhandler für die Klicks auf die Ellipse-Schaltfläche (Spalte 2 und 3)
  void __fastcall EllipseBtnClicked(TObject *Sender);
  // Eventhandler für Doppelklicks auf den Monatskalender
  void __fastcall MonthCalendarDblClick(TObject *Sender);
  // Füllen der ComboBox mit "Abwesenheitsgründen"
  void __fastcall LoadReasonsToCombo(TComboBox *pComboBox);
  // Füllen der ComboBox mit den Namen
  void __fastcall LoadNamesToCombo(TComboBox *pComboBox);

public:    // Anwender-Deklarationen
  __fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif

//-----  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)
{
  // ComboBox für die Bearbeteung der Einträge in
  // der 1. Spalte vorbereiten:
  pComboEditor = new TComboBox(this);
  pComboEditor->Parent = this;
  pComboEditor->Visible = false;
  pComboEditor->Style = csDropDownList;
  pComboEditor->OnChange = ComboEditorChange;

  // Schaltfläche für die Bearbeteung der Einträge in
  // der 1. Spalte vorbereiten:
  pEllipseBtn = new TButton(this);
  pEllipseBtn->Parent = this;
  pEllipseBtn->Visible = false;
  pEllipseBtn->TabStop = false;
  pEllipseBtn->OnClick = EllipseBtnClicked;

  pMonthCalendar = NULL;

  // Spaltenüberschriften eintragen:
  StringGrid1->Cells[0][0] = "Grund";
  StringGrid1->Cells[1][0] = "Von";
  StringGrid1->Cells[2][0] = "Bis";
  StringGrid1->Cells[3][0] = "Genehmigt";
  StringGrid1->Cells[4][0] = "Bemerkung";

  // Tabelle mit Beispieldaten füllen
  StringGrid1->Cells[0][1] = "Sonderurlaub";
  StringGrid1->Cells[1][1] = TDateTime(Now()+3).FormatString("dd.mm.yyyy");
  StringGrid1->Cells[2][1] = TDateTime(Now()+10).FormatString("dd.mm.yyyy");
  StringGrid1->Cells[3][1] = "Müller";
  StringGrid1->Cells[4][1] = "Umzug";

  StringGrid1->Cells[0][2] = "Fortbildung";
  StringGrid1->Cells[1][2] = TDateTime(Now()+20).FormatString("dd.mm.yyyy");
  StringGrid1->Cells[2][2] = TDateTime(Now()+22).FormatString("dd.mm.yyyy");
  StringGrid1->Cells[3][2] = "Schmidt";
  StringGrid1->Cells[4][2] = "Fa. Xyz, Frankfurt";

}


//---------------------------------------------------------------------------
// OnClick-Eventhandler der Tabelle
//---------------------------------------------------------------------------
void __fastcall TForm1::StringGrid1Click(TObject *Sender)
{
  // Sender-Zeiger casten:
  TStringGrid* pGrid = dynamic_cast<TStringGrid*>(Sender);

  // Falls 5. Spalte, direkte Bearbeitung der Zelle erlauben:
  if(pGrid->Col == 4) pGrid->Options  = pGrid->Options << goEditing;
  else pGrid->Options  = pGrid->Options >> goEditing;

  // Falls Spalte 1. oder 4.
  if(pGrid->Col == 0 || pGrid->Col == 3)
  {
    // die Inplace-Combobox füllen:
    if(pGrid->Col == 0)
      LoadReasonsToCombo(pComboEditor);
    else if(pGrid->Col == 3)
      LoadNamesToCombo(pComboEditor);
    // die Inplace-Combobox in der
    // selektierten Zelle anzeigen:
    PlaceComboEditor();

    // Falls der Zelleninhalt in der ComboBox vorhanden ist,
    // entsprechendes Item in der ComboBox anzeigen:
    if(pComboEditor->Items->IndexOf(pGrid->Cells[pGrid->Col][pGrid->Row]) >= 0)
      pComboEditor->ItemIndex = pComboEditor->Items->
        IndexOf(pGrid->Cells[pGrid->Col][pGrid->Row]);
    // falls die Ellipse-Schaltfläche sichtbar, ausblenden:
    if(pEllipseBtn->Visible) pEllipseBtn->Visible = false;
  }
  // falls Spalte 2 oder 3 angeklickt:
  else if(StringGrid1->Col == 1 || StringGrid1->Col == 2)
  {
    // die Ellipse-Schaltfläche in der Zelle positionieren/anzeigen:
    PlaceEllipseBtn();
    // falls die Inplace-ComboBox sichtbar, ausblenden:
    if(pComboEditor->Visible) pComboEditor->Visible = false;
  }
  else
  {
    pComboEditor->Visible = false;
    pEllipseBtn->Visible = false;
  }

  // Kalender ggf. ausblenden:
  if(pMonthCalendar && pMonthCalendar->Visible)
    pMonthCalendar->Visible = false;
}

//---------------------------------------------------------------------------
// Füllen der ComboBox mit "Abwesenheitsgründen"
// (wird von der Funktion StringGrid1Click() aufgerufen)
//---------------------------------------------------------------------------
void __fastcall TForm1::LoadReasonsToCombo(TComboBox *pComboBox)
{
  pComboBox->Clear();
  pComboEditor->Items->Add("Tarifurlaub");
  pComboEditor->Items->Add("Sonderurlaub");
  pComboEditor->Items->Add("Gleitzeit");
  pComboEditor->Items->Add("Fortbildung");
}

//---------------------------------------------------------------------------
// Füllen der ComboBox mit den Namen
// (wird von der Funktion StringGrid1Click() aufgerufen)
//---------------------------------------------------------------------------
void __fastcall TForm1::LoadNamesToCombo(TComboBox *pComboBox)
{
  pComboBox->Clear();
  pComboEditor->Items->Add("Meier");
  pComboEditor->Items->Add("Müller");
  pComboEditor->Items->Add("Schmidt");
  pComboEditor->Items->Add("Hoffmann");
}

//---------------------------------------------------------------------------
// Beim Scrollen der Tabelle Steuerelemnte ggf. neu positionieren
//---------------------------------------------------------------------------
void __fastcall TForm1::StringGrid1TopLeftChanged(TObject *Sender)
{
  StringGrid1->Invalidate();
  if(pMonthCalendar)  pMonthCalendar->Visible = false;
  if(StringGrid1->Col == 0 || StringGrid1->Col == 3)
    PlaceComboEditor();
  else if(StringGrid1->Col == 1 || StringGrid1->Col == 2)
    PlaceEllipseBtn();
}

//---------------------------------------------------------------------------
// Positionierung der "..."-Schaltfläche in der ausgewählten Zelle
//---------------------------------------------------------------------------
void __fastcall TForm1::PlaceEllipseBtn(void)
{
  TRect Rect = StringGrid1->CellRect(StringGrid1->Col,StringGrid1->Row);
  pEllipseBtn->Height = Rect.Bottom-Rect.Top-2*StringGrid1->GridLineWidth;
  pEllipseBtn->Width = pEllipseBtn->Height;
  pEllipseBtn->Caption = "...";
  pEllipseBtn->Top = Rect.Top + StringGrid1->GridLineWidth * 2 + 1;
  pEllipseBtn->Left = Rect.Right - pEllipseBtn->Width;
  pEllipseBtn->Visible = true;
}

//---------------------------------------------------------------------------
// Positionierung der ComboBox in der ausgewählten Zelle
//---------------------------------------------------------------------------
void __fastcall TForm1::PlaceComboEditor(void)
{
  TRect Rect = StringGrid1->CellRect(StringGrid1->Col,StringGrid1->Row);
  pComboEditor->Top = StringGrid1->Top;
  pComboEditor->Left = StringGrid1->Left;
  pComboEditor->Text = EmptyStr;
  pComboEditor->Top = pComboEditor->Top + Rect.Top +
    StringGrid1->GridLineWidth;
  pComboEditor->Left = pComboEditor->Left +
    Rect.Left + StringGrid1->GridLineWidth+1;
  pComboEditor->Height = (Rect.Bottom - Rect.Top) + 1;
  pComboEditor->Width = (Rect.Right - Rect.Left) + 0;
  pComboEditor->Visible = true;
  pComboEditor->SetFocus();
}

//---------------------------------------------------------------------------
// Eventhandler für die Klicks auf die Ellipse-Schaltfläche (Spalte 2 und 3)
//---------------------------------------------------------------------------
void __fastcall TForm1::EllipseBtnClicked(TObject *Sender)
{
  TRect Rect = StringGrid1->CellRect(StringGrid1->Col,StringGrid1->Row);

  // falls der Zeiger auf den Monatskalender noch NULL,
  // Monatskalender (TMonthCalendar) instanziieren:
  if(pMonthCalendar == NULL)
  {
    pMonthCalendar = new TMonthCalendar(this);
    pMonthCalendar->Parent = this;

    // Doppelklick-Ereignisbehandlungsroutine zuweisen:
    pMonthCalendar->OnDblClick = MonthCalendarDblClick;
  }

  // Monatskalender unter der selektierten Zelle anzeigen:
  if(pMonthCalendar)
  {
    pMonthCalendar->Top = Rect.Bottom + StringGrid1->GridLineWidth*2;
    pMonthCalendar->Left = Rect.Left + StringGrid1->GridLineWidth*2;
    AnsiString slCellText = StringGrid1->Cells[
      StringGrid1->Col][StringGrid1->Row];
    // Falls die Zell nicht leer,
    if(ConvertToDateTime(slCellText) != TDateTime(0))
      // die Datumsangabe in der Zelle der Date-Eigenschaft
      // des TMonthCalendar zuweisen:
      pMonthCalendar->Date = ConvertToDateTime(slCellText);
    else pMonthCalendar->Date = Now();
    pMonthCalendar->Visible = true;
  }
}

//---------------------------------------------------------------------------
// Eventhandler für die Änderung der Auswahl in der Inplace-ComboBox
//---------------------------------------------------------------------------
void __fastcall TForm1::ComboEditorChange(TObject *Sender)
{
  // den in der ComboBox selektirten String in die Zelle übernehmen:
  StringGrid1->Cells[StringGrid1->Col][StringGrid1->Row] =
     pComboEditor->Items->Strings[pComboEditor->ItemIndex];
}


//---------------------------------------------------------------------------
// Eventhandler für Doppelklicks auf den Monatskalender
//---------------------------------------------------------------------------
void __fastcall TForm1::MonthCalendarDblClick(TObject *Sender)
{
  // das selektirte Datum in die Zelle übernehmen:
  StringGrid1->Cells[StringGrid1->Col][StringGrid1->Row] =
    pMonthCalendar->Date.FormatString("dd.mm.yyyy");
  pMonthCalendar->Visible = false;
  StringGrid1->SetFocus();
}

//---------------------------------------------------------------------------
// Konvertiert Datumsstring (Format: "dd.mm.yyyy") zum TDateTime-Object
//---------------------------------------------------------------------------
TDateTime ConvertToDateTime(AnsiString slDateStr)
{
  TDateTime dtRetVal = TDateTime(0);
  if(slDateStr.Length() == 10)
  {
    dtRetVal = TDateTime(
      short(slDateStr.SubString(7,4).ToIntDef(0)),
      short(slDateStr.SubString(4,2).ToIntDef(0)),
      short(slDateStr.SubString(1,2).ToIntDef(0)));
  }
  return dtRetVal;
}

//---------------------------------------------------------------------------
// OnResize-Eventhandler des Formulars
//---------------------------------------------------------------------------
void __fastcall TForm1::FormResize(TObject *Sender)
{
  // Breite der letzten Spalte an die Formularbreite anpassen:
  int ilColWidthsSum = 0;
  for(int ilCol = 0; ilCol < StringGrid1->ColCount; ilCol++)
    ilColWidthsSum+=StringGrid1->ColWidths[ilCol]+StringGrid1->GridLineWidth;

  if(ilColWidthsSum < StringGrid1->ClientWidth)
    StringGrid1->ColWidths[StringGrid1->ColCount-1] =
      StringGrid1->ClientWidth - ilColWidthsSum +
        StringGrid1->ColWidths[StringGrid1->ColCount-1];
}

  Download BCB5
Projekt-Quellcode
Download
Demo-Exe

© '99-2002 by S. Kreutzmann