C++ Builder Snippets  


Komponenten auf dem Formular zur Laufzeit verschieben, ihre Positionen speichern und wiederherstellen:
 
In bestimmten Fällen ist es erforderlich, dem Anwender die Möglichkeit zu geben, die Anordnung der Steuerelemente auf dem Formular selbständig zu verändern. Folgendes Beispiel zeigt eine der Möglichkeiten die Komponenten zur Programmlaufzeit mit der Maus zu verschieben. Die Positionen der Steuerelemente werden beim Beenden des Programms in einer INI-Datei abgelegt und beim nächsten Programmstart automatisch wiederhergestellt.
Während das Abspeichern und das Wiederherstellen der Positionen der Steuerlemente mit VCL-Mitteln realisiert ist, musste zum Verschieben der Komponenten ein kleiner API-Trick verwendet werden: mit Hilfe der Windows-Botschaft WM_SYSCOMMAND mit WParam = SC_MOVE + HTCAPTION, die an das zu verschiebende Steuerelement gesendet wird, wird das Control in einen Zustand versetzt, der eigentlich zum Verschieben von Fenstern vorgesehen ist. Da viele VCL-Steuerelemente fensterorientiert sind (und ein Window-Handle besitzen) kann diese Vorgehensweise auf sie angewandt werden.

Im Vergleich zur pixelweisen Verschiebung von Komponenten in OnMouseMove-Ereignis bietet diese Methode einige Vorteile: während des Ziehens werden die Controls "flimmerfrei" gezeichnet und man ist nicht an das Vorhandensein der OnMoseDown/OnMoseMove/OnMoseUp-Ereignisse bei den zu verschiebenden Komponenten angewiesen (aus diesem Grund wird in dem Beispiel unten eine eigene Eriegnisbehandlung für die WM_LBUTTONDOWN-Botschaft verwendet).

Es gibt allerdings eine ganze Reihe von Komponenten, die von TGraphicControl abgeleitet (und somit nicht fensterorientiert) sind, z.B.: Tlabel, TBevel, TImage, TPaintBox, TShape, TSpeedButton etc. Um diese Steuerelemente auf die gleiche Weise zu verschieben, werden diese in unserem Beispiel beim Anklicken temporär auf ein unsichtbares TPanel (Hilfspanel) gesetzt, das dann frei bewegt werden kann. Nach dem Abschluß der Positionierung wird das Steuerelement wieder der ursprünglich übergeordneten Komponente zugewiesen.

//-------- in der Header-Datei frmMain.h --------------

class TForm1 : public TForm
{

...

private:

  // Zeiger auf Hilfs-TPanel zum Verschieben der Elemente:
  TPanel* pHelpPanel;

public:

  // Botschaftsbehandlungsroutine für die Application
  void __fastcall OnAppMessage(tagMSG &Message, bool &Handled);

  // Funktion fürs Verschieben der Steuerelemente
  void __fastcall MoveControl(TControl* Sender);

  // Schreibt Positionen der Steuerelemente in die Ini-Datei
  bool SaveControlsPositions(void);

  // Positionen der Steuerelemente aus der Ini-Datei
  // lesen und wiederherstellen
  bool RestoreControlsPositions(void);
...

};

//--------------------------------------------------------


//-------- frmMain.cpp --------------
//---------------------------------------------------------------------------
// OnShow-Eventhandler des Formulars
//---------------------------------------------------------------------------
void __fastcall TForm1::FormShow(TObject *Sender)
{
  // Hilfspanel zum Verschieben der Steuerelemente erzeugen
  pHelpPanel = new TPanel(this);

  pHelpPanel->Parent = this;
  pHelpPanel->BorderStyle = bsNone;
  pHelpPanel->BevelInner = bvNone;
  pHelpPanel->BevelOuter = bvNone;
  pHelpPanel->Caption = "";
  pHelpPanel->FullRepaint = false;
  pHelpPanel->Visible = false;

  // eigene Botschaftsbehandlungsroutine der Application zuweisen
  Application->OnMessage = OnAppMessage;

  // Positionen der Steuerelemente aus der Ini-Datei lesen und wiederherstellen
  RestoreControlsPositions();

}

//---------------------------------------------------------------------------
// Botschaftsbehandlungsroutine der Application zum
// Abfangen der Klicks mit der linken Maustaste
//---------------------------------------------------------------------------
void __fastcall TForm1::OnAppMessage(tagMSG &Message, bool &Handled)
{
  // nur falls die linke Maustaste gedrückt wurde:
  if(Message.message == WM_LBUTTONDOWN)
  {
    // Zeiger auf das Steuerelement unter dem Mauscursor besorgen:
    TControl * pControl = ControlAtPos(ScreenToClient(
      Message.pt), false, true);

    // falls dieses Steuerelement NICHT die Checkbox "Steuerelemente
    // verschiebbar" ist und diese angeklickt ist:
    if(pControl != chkAllowMove && chkAllowMove->Checked)
    {
      // Falls das Steuerelement weitere untergeordnete Elemente
      // beinhaltet, prüfen ob nicht eines davon verschoben werden soll:
      if(pControl)
      {
        TWinControl* pCmp = dynamic_cast<TWinControl*>(pControl);
        TControl*    pCtrl;
        while(pCmp)
        {
          pCtrl = dynamic_cast<TControl*>(pCmp->ControlAtPos(pCmp->
            ScreenToClient(Message.pt), false, true));
          pCmp = dynamic_cast<TWinControl*>(pCtrl);
          if(pCmp) pControl = pCmp;
          else if(pCtrl) pControl = pCtrl;
        }
      }

      // Control verschieben:
      MoveControl(pControl);
      Handled = true;
    }
  }
}

//---------------------------------------------------------------------------
// Funktion fürs Verschieben der Steuerelemente mit der Maus
//---------------------------------------------------------------------------
void __fastcall TForm1::MoveControl(TControl* pControl)
{
  // versuchen den Komponentenzeiger in TWinControl (Zeiger auf ein
  // fensterorientiertes Steuerelement) umzucasten:
  TWinControl* pWinControl = dynamic_cast<TWinControl*>(pControl);

  // falls Umwandlung erfolgreich (Steuerelement fensterorientiert)
  if(pWinControl)
  {
    // "Verschieben"-Modus für das Steuerlement aktivieren:
    ReleaseCapture();
    pWinControl->Perform(WM_SYSCOMMAND, SC_MOVE+HTCAPTION, 0);
  }
  // Falls ein NICHTfensterorientierte Komponente
  // verschoben werden soll:
  else if(pControl)
  {
    // Hilfspanel entsprechend der Position und
    // Grösse der Komponente ausrichten:
    pHelpPanel->Parent = pControl->Parent;
    pHelpPanel->Left   = pControl->Left;
    pHelpPanel->Top    = pControl->Top;
    pHelpPanel->Width  = pControl->Width;
    pHelpPanel->Height = pControl->Height;
    // Das zu verschiebende Steuerelement auf
    // dem Hilfspanel platzieren:
    TWinControl* pSaveParent = pControl->Parent;
    pControl->Parent   = pHelpPanel;
    pControl->Left     = 0;
    pControl->Top      = 0;
    pHelpPanel->Visible = true;
    Application->ProcessMessages();

    // "Verschieben"-Modus für das Hilfspanel aktivieren:
    ReleaseCapture();
    pHelpPanel->Perform(WM_SYSCOMMAND, SC_MOVE+HTCAPTION, 0);

    // Nach dem Verschieben das Steuerelement an der
    // Zielposition auf dem Formular platzieren:
    pControl->Parent = pSaveParent;  // this = Zeiger auf die frmMain
    pControl->Left = pHelpPanel->Left;
    pControl->Top = pHelpPanel->Top;
    pHelpPanel->Visible = false; // Hilfspanel ausblenden
  }
}

//---------------------------------------------------------------------------
// OnDestroy-Eventhandler des Formulars
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
  // Positionen der Steuerelemente speichern:
  SaveControlsPositions();
}


//------------------------------------------------------------------------
// Schreibt Positionen der Steuerelemente in die Ini-Datei
//------------------------------------------------------------------------
bool TForm1::SaveControlsPositions(void)
{
  bool blReturnValue = true;
  TControl* pCtrl = NULL;
  AnsiString slIniFileName = ExtractFilePath(ParamStr(0)) + "ctrl_pos.ini";
  TMemIniFile* pIni = new TMemIniFile(slIniFileName);
  if(pIni != NULL)
  {
    try
    {
      for(int ilCmpNo = 0; ilCmpNo < this -> ComponentCount; ilCmpNo++)
      {
        pCtrl =  dynamic_cast<TControl*>(this -> Components[ilCmpNo]);
        if(pCtrl != NULL && pCtrl != pHelpPanel && pCtrl != chkAllowMove)
        {
          pIni -> WriteInteger(pCtrl->Name, "Left", pCtrl->Left);
          pIni -> WriteInteger(pCtrl->Name, "Top", pCtrl->Top);
        }
      }
      pIni -> UpdateFile();
      blReturnValue = true;
    }
    __finally
    {
      if(pIni != NULL) { delete pIni; pIni = NULL; }
    }
  }
  else blReturnValue = false;
  return blReturnValue;
}


//---------------------------------------------------------------------------
// Positionen der Steuerelemente aus der Ini-Datei lesen und wiederherstellen
//---------------------------------------------------------------------------
bool TForm1::RestoreControlsPositions(void)
{
  bool blReturnValue = true;
  TControl* pCtrl = NULL;
  AnsiString slIniFileName = ExtractFilePath(ParamStr(0)) + "ctrl_pos.ini";
  if(!FileExists(slIniFileName))
    return false;
  else
  {
    TMemIniFile* pIni = new TMemIniFile(slIniFileName);
    if(pIni != NULL)
    {
      // Sections-Stringliste erzeugen:
      TStringList * sllSections = new TStringList;
      try
      {
        // Namen der Steuerelemente einlesen:
        pIni -> ReadSections(sllSections);
        for(int ilSectionNo = 0; ilSectionNo < sllSections -> Count;
          ilSectionNo++)
        {
          pCtrl = dynamic_cast<TControl*>(this -> FindComponent(
            sllSections ->Strings[ilSectionNo]));
          if(pCtrl != NULL)
          {
            pCtrl->Left = pIni -> ReadInteger(pCtrl->Name, "Left", pCtrl->Left);
            pCtrl->Top = pIni -> ReadInteger(pCtrl->Name, "Top", pCtrl->Top);
          }
        }
        blReturnValue = true;
      }
      __finally
      {
        if(sllSections != NULL) { delete sllSections; sllSections = NULL; }
        if(pIni != NULL) { delete pIni; pIni = NULL; }
      }
    }
  }
  return blReturnValue;
}
//---------------------------------------------------------------------------


  Download BCB5
Projekt-Quellcode
Download
Demo-Exe

© '99-2002 by S. Kreutzmann