Programmieren mit C++

Code-Beispiele & Lösungen

Allgemeine visuelle Objektklassen

Drag-fähige Listbox

Die Klasse TDragListBox bietet eine Drag-fähige Listbox, deren Elemente per Maus innerhalb der Listbox verschoben werden können. Damit kann die Reihenfolge der Listenelemente beliebig per Drag&Drop geändert werden. Allerdings setzt diese Klasse voraus, daß die Listbox ohne das Attribut LBS_SORT erzeugt wurde, da ansonsten die Reihenfolge der Listboxeinträge durch die Sortierung vorgegeben ist.

TDragListBox(TWindow * par_win, 
             int res_id)

Der Konstruktor TDragListBox erzeugt ein Objekt der Klasse TDragListBox, das dem Ressourcenelement res_id des Fensters zugeordnet ist.

Zu den Aufgaben des Konstruktors gehört es auch, zwei Zeiger-Bitmaps zu laden, die den normalen und ziehenden Zustand anzeigen.

Konstruktor TDragListBox

~TDragListBox (void)

Der Destruktor zerstört die durch den Konstruktor geladenen Cursor-Bitmaps.

Destruktor TDragListBox

void TDragListBox::SetupWindow (void)

Die Memberfunktion SetupWindow wurde überschrieben, um die Höhe eines Eintrag ermitteln und in Height speichern zu können. Der Wert wird für die Berechnung der Positionen innerhalb der Listbox benötigt.

Interna der Implementierung

Neben den beschriebenen Memberfunktionen veröffentlicht die Klasse TDragListBox keine weiteren Funktionen. Für die Realisation der Funktionalität werden nur drei weitere private Funktionen benötigt, die auf die Mausereignisse reagieren:

  • EvLButtonDown
  • EvMouseMove
  • EvLButtonUp

Memberfunktion SetupWindow

Der Ziehvorgang wird durch das Drücken der linken Maustaste eingeleitet. Der entsprechende Handler überprüft zunächst, ob zumindest ein Listboxeintrag selektiert ist:

void TDragListBox::EvLButtonDown(
       uint modKeys, TPoint & pt)
{
 TRect rect;
 TListBox::EvLButtonDown(modKeys, pt);
 if (GetCount() == 1)
  return;

Anschließend wird der Clientbereich der Listbox bestimmt und der Mausbewegungsbereich beschränkt sowie das interne Flag MouseDown gesetzt:

 rect = GetClientRect();
 CurrentTop = GetTopIndex();
 int index = pt.y / Height;
 CurrentSelection = index + CurrentTop;
 GetItemRect(index, CurrentEntryRect);
 CurrentEntryRect.bottom = CurrentEntryRect.top + Height;
 MouseDown = true;
 SetCapture();
 rect = GetWindowRect();
 rect.bottom++;
 ClipCursor(&rect);
}

Ziehvorgang einleiten

Der Eventhandler für das Ereignis „Mausbewegung“ testet zunächst, ob ein Ziehvorgang eingeleitet war, was sich anhand des Flags MouseDown ergibt. Liegt der neue Mauspunkt außerhalb des Rechtecks des Eintrags, wechselt der Mauszeiger auf den Ziehcursor, der den Ziehvorgang symbolisieren soll.

void TDragListBox::EvMouseMove(
      uint modKeys, TPoint & pt)
{
 if (MouseDown)  
{
  if (!PtInRect(&CurrentEntryRect, pt))
   ::SetCursor(DragCursor);

Im nächsten Schritt wird die neue Eintragsposition bestimmt und ggfs. die oberste Zeile der Listbox angepaßt:

  TRect rect = GetClientRect();
  int entries = (GetCount());
  int bottom = rect.bottom / Height + CurrentTop;
  if (pt.y < rect.top && CurrentTop > 0)   
   {
   SetCaretIndex(CurrentTop - 1, false);
   CurrentTop--;
  }
  else if (pt.y > rect.bottom && bottom < entries)   
   {
   SetTopIndex(CurrentTop + 1);
   SetCaretIndex(bottom + 1, false);
   CurrentTop++;
  }
  else {
   SetCaretIndex(pt.y / Height + CurrentTop, false);
  }

Abschließend wird die geerbte Methode aufgerufen:

  TListBox::EvMouseMove(modKeys, pt);
 }

Liegt kein Ziehvorgang vor, wird gleich zur geerbten Methode verzweigt:

 else
  TListBox::EvMouseMove(modKeys, pt);
}

Mausbewegung

Wird die linke Maustaste losgelassen, muß zunächst überprüft werden, ob ein Ziehvorgang zu bearbeiten ist. Falls nicht, wird die geerbte Methode aufgerufen und die Funktion beendet:

void TDragListBox::EvLButtonUp (
        uint modKeys, TPoint & pt)
{
 if (!MouseDown)
 {
  TListBox::EvLButtonUp(modKeys, pt);
  return;
 }

Im Falle eines aktiven Ziehvorgangs wird zunächst das Flag MouseDown zurückgesetzt und die Bewegungsbeschränkung der Maus aufgehoben:

 MouseDown = false;
 ReleaseCapture();
 ClipCursor(NULL);

Anschließend kann die neue Position des selektierten Eintrags bestimmt werden:

 int newPosition = (pt.y / Height + CurrentTop);
 int entries = GetCount();
 if (newPosition == entries)
  newPosition = -1;

Das Verschieben des selektierten Eintrags erfolgt durch Löschen des Eintrags an seiner aktuellen Position und einem Wiedereinfügen an der Zielposition. Dazu muß der Eintrag temporär zwischengespeichert werden, da die Listbox über keine Möglichkeit zur Zwischenspeicherung oder gar Indexänderung verfügt:

 char buf[256];
 GetString(buf, CurrentSelection);
 DeleteString(CurrentSelection);
 int result = InsertString(buf, newPosition);
 SetSelIndex(result);

Abschließend wird die geerbte Methode EvLButtonUp aufgerufen:

 TListBox::EvLButtonUp(modKeys, pt);
}

Erweiterungen

Für die vorgestellte Lösung sind diverse Erweiterungen denkbar. So könnte beispielsweise das Drag&Drop auf mehrere Einträge erweitert werden. Dazu müßten in der Methode EvLButtonUp die Einträge in einer Schleife verschoben werden, indem die selektierten Einträge nacheinander über das beschriebene Löschen und Wiedereinfügen umplaziert werden. Allerdings sind dabei erweiterte Bedingungen beim Selektieren und Bestimmen der neuen Position zu beachten.

Was die vorgestellte Lösung ebenfalls nicht berücksichtigt, sind benutzergezeichnete Einträge. Die Klasse TDragListBox setzt voraus, daß es sich bei den Einträgen um Strings handelt.

Mit der vorgestellten Klasse TDragListBox liegt jedoch eine Musterlösung vor, die mit wenig Aufwand an die eigenen Bedürfnisse angepaßt werden kann.

Ziehvorgang beenden





Sachgebiet


© 2009-2012 by Alojado Publishing. Alle Rechte vorbehalten. Ausgewiesene Marken gehören ihren jeweiligen Eigentümern.
Mit der Benutzung dieser Seite erkennen Sie die Nutzungsbedingungen und die Datenschutzerklärung an. Der Betreiber übernimmt keine Haftung für den Inhalt verlinkter externer Internetseiten.
Seite erzeugt 2012-05-20 01:54:17 von textarchiv.alojado.de