Eine sortierte ListBox ist ja gut und schön - und nullkommanix
eingerichtet, wenn Sie die Sorted-Eigenschaft auf True setzen. Dann
werden Die Einträge in der ListBox fein säuberlich sortiert
dargestellt und nachträglich neu hinzukommende Einträge passend
einsortiert - nach allen Regeln der... ja, nach welchen Regeln
eigentlich? Zunächst brav alphabetisch bzw. im Prinzip lexikalisch.
Das bedeutet, dass Umlaute usw. wie der Stammlaut des Umlauts (ä
wie a usw.) behandelt werden - es gibt aber auch andere
Sortierregeln. Doch bei Zahlen sieht die Sortierung etwas eigenartig
aus: 1 kommt vor 10 kommt vor 2 kommt vor 20... Das ist zwar nach
der schlichten Regel der alphabetischen Einordnung von Ziffern und
Zahlen völlig korrekt, aber ganz und gar nicht zufriedenstellend.
Bei Datums-Angaben kommt die Sortierung gleichfalls gehörig ins
Schleudern.
Sie können sich natürlich hinsetzen, ein wenig
Informatik-Grundkenntnisse pauken und an Sortier-Algorithmen mit so
klingenden Namen wie Quick-Sort, Bubble-Sort und dergleichen
herumtüfteln. Fertigen Visual Basic-Code für diese Algorithmen
werden Sie schnell finden, die Anpassung eine ListBox und die
Einbeziehung derselben wird Ihnen dann allerhöchstens ein paar
wenige Stunden Kopfzerbrechen bereiten. Den gleichen Weg werden Sie
offensichtlich beschreiten müssen, wenn Sie die Einträge in der
ListBox nach Kriterien sortieren wollen, die nicht auf dem
dargestellten Text der Einträge, sondern etwa auf dem Inhalt der
ItemData-Werte, die Sie ja jedem ListBox-Eintrag zuordnen können.
Klingt Ihnen das zu kompliziert? Nun, dann benutzen Sie doch
einfach trotzdem eine sortierte ListBox. Sie stellen einfach das
Sortierkriterium bzw. den Sortierschlüssel vor den eigentlichen
Text eines ListBox-Eintrags, setzen ein Tabulatorzeichen (vbTab)
dazwischen und... Sieht nicht so toll aus, finden Sie? Sie haben
recht, der Anwender soll ja die Sortierschlüssel nicht sehen. Wenn
es aber eine Möglichkeit gäbe, die Einträge so weit nach links
aus der ListBox heraus zu verschieben, bis die vorangestellten
Sortierschlüssel verschwunden sind, hätten wir die Lösung. Leider
gibt es eine solche Möglichkeit nicht. Es sei denn, Sie fangen an,
die ListBox-Einträge selbst zu zeichnen - mit ein "paar"
API-Techniken ist das durchaus möglich. Aber dann könnten Sie auch
ebenso den Weg der manuellen Sortiererei beschreiten, der Aufwand
ist in beiden Fällen nicht trivial und hält sich die Waage. Der
Ansatz, die ListBox-Einträge zu verschieben, fällt also wohl unter
den Tisch.
Die relativ einfach zu bewerkstelligende Lösung lautet: Sie
decken den Teil der Sortierschlüssel der ListBox-Einträge ab. Ganz
so trivial, wie einfach eine PictureBox oder dergleichen über die
linke Seite der ListBox zu legen, ist das natürlich nicht. Der
ListBox käme ja der linke Rand abhanden - wie sähe denn das bloß
aus...!
Aber mit Hilfe von UserControls kommen Sie zum Ziel. Ein
UserControl (das eigentliche, öffentliche "ListBoxMask"
genannte Control) enthält weiteres UserControl ("ucBorder"
genannt und nicht öffentlich) mit einem Rahmen (BorderStyle =
1), das den neuen linken Teil des ListBox-Rahmens liefert.
Dieses UserControl mit dem Rahmen (ucBorder) wird bis knapp an die
rechte Kante des Träger-UserControls (ListBoxMask) geschoben, wobei
sein rechter Rand beliebig weit nach rechts ins Leere hinausragt,
aber seine linke Kante noch vollständig sichtbar ist. Diese
Anordnung wird automatisch im Ereignis UserControl_Resize von
ListBoxMask vorgenommen.
Jetzt brauchen Sie dieses ListBoxMask-Control nur noch über eine
ListBox zu legen und die Höhe und die linke Kante mit dieser in
Übereinstimmung zu bringen. Durch Verschieben der rechten Kante von
ListBoxMask stellen Sie nun ganz einfach die gewünschte
Überdeckung ein.
Als Voreinstellung der Maskenfarbe wird beim ersten Anlegen eines
ListBoxMask-Controls die Hintergrundfarbe des Containers (Ambient.BackColor)
automatisch vorgewählt. Als Hintergrundfarbe (Füllfarbe) des
Rahmen-Controls ucBorder ist vbWindowBackground voreingestellt - das
entspricht der voreingestellten Hintergrundfarbe einer ListBox.
Beide Farben können Sie in den Eigenschaften MaskColor und
MarginColor beliebig ändern. Letztere Eigenschaft habe ich
MarginColor genannt, weil Sie über die Eigenschaft MarginWidth
festlegen können, wie viel vom ucBorder-Control sichtbar werden
soll. Ist es breiter, als seine reine Rahmenbreite, wirkt seine
Füllung wie eine linke Randspalte, die in der Hintergrundfarbe der
ListBox eine weitere Abdeckungsmöglichkeit bietet. Allerdings
reichen dann sowohl Selektionsbalken als auch das Fokus-Rechteck
nicht mehr bis an den linken Rahmen heran und wirken abgeschnitten.
Ich wollte Ihnen jedoch nicht diese zusätzliche
Gestaltungsmöglichkeit vorenthalten.
Über die Eigenschaft Appearance können Sie die
Rahmendarstellung an die der ListBox anpassen. In der Einstellung
lbm3D sorgt Sie wie bei der ListBox für die übliche plastische
Darstellung, in der Einstellung lbm2D für eine flache Kontur.
Zur Kontrolle der Überdeckung können Sie die Eigenschaft
ShowMask auf False setzen - das ListBoxMask-Control wird
transparent. Im Entwicklungsmodus zeigt eine Linie am rechten Rand
die Überdeckung an, zur Laufzeit ist diese Linie nicht sichtbar -
die Wirkung ist dieselbe, als ob Sie das Control über seine
Visible-Eigenschaft unsichtbar gemacht hätten.
Die Maskierungsfläche ist natürlich keine tote Fläche in Ihren
Forms. Sie können zum einen die ListBox samt ListBoxMask-Control
soweit nach links auch aus dem Form hinausschieben, bis Sie eine
"normale" ListBox-Position erhalten. Sie können auch
andere Steuerelemente über der Maskierungsfläche anordnen. Da
ListBoxMask allerdings ein vollwertiges Steuerelement sein muss, um
eine ListBox überdecken zu können, kann es nicht von den
grafischen Steuerelementen wie Shape, Line und Label überdeckt
werden. Da es aber als Control-Container ausgelegt ist, kann es
solche Steuerelemente selbst aufnehmen. Diese können dann
allerdings nicht über die Grenzen von ListBoxMask hinausragen.
Sollte dies für die Gestaltung Ihres Forms notwendig sein, müssen
Sie wohl oder übel ein Frame-Steuerelement mit BorderStyle gleich
0 darüber legen und zur Positionierung grafischer
Steuerelemente verwenden, oder diese gegebenenfalls so zu sagen
"gestückelt" anordnen - ein Element innerhalb von
ListBoxMask, ein weiteres exakt angepasst außerhalb angesetzt.
Außerdem werden alle Maus-relevanten Ereignisse wie Click,
DblClick, MouseDown , MouseMove und MouseUp weitergereicht und
gleich in die Koordinaten des Containers umgerechnet. Wundern Sie
sich aber nicht, dass die X und Y-Werte "exakter" als die
des umgebenden Forms erscheinen - Visual Basic rechnet intern etwas
anders als das COM-System, das für Steuerelemente zuständig ist.
Dieses rechnet mit einer wesentlich höheren Auflösung (vbHimetric).
Ginge es nur um die gegebenenfalls notwendige einfache Umrechnung
von Pixels in Twips, hätte ich eine VB-analoge Rundung einbauen
können. Da sich die Umrechnung jedoch flexibel jedem Container
anpassen sollte, habe ich dies bewusst unterlassen (private Funktion
zConvertToContainer). Sie können die Single-Werte der X- und
Y-Koordinaten selbst per Konvertierung mit CLng glätten.
Schließlich werden auch noch die Ereignisse OLEDragOver und
OLEDragDrop weitergereicht, wenn Sie in der Eigenschaft OLEDropMode
lbmOLEDropManual wählen, oder wenn Sie lbmOLEDropContainer wählen
und der Container entsprechend eingestellt ist.
Beim Auslesen der ListBox-Einträge mit dem vorangestellten
Schlüssel ist auch nicht weiter schwierig. Sie suchen die Position
des Tabulator-Zeichens und trennen den Eintrag an dieser Stelle in
Schlüssel und eigentlichen Eintrag:
Dim nKey As String
Dim nValue As String
Dim nItem As String
Dim nTabPos As Integer
nItem = ListBox.List(Index)
nTabPos = InStr(nItem, vbTab)
nKey = Left$(nItem, nTabPos -1)
nValue = Mid$(nItem, nTabPos + 1)
Das ListBoxMask-Control ist beidermaßen für Visual Basic
5 als auch Visual Basic 6 geeignet. Da jedoch in
VB 5 die Appearance-Eigenschaft zur Laufzeit
schreibgeschützt ist, habe ich zwei verschiedene Versionen daraus
gemacht. Leider gilt also für die VB 5-Version der
obenstehende Abschnitt zur Eigenschaft Appearance nicht. Wenn Sie
dennoch unbedingt eine 2D-Darstellung brauchen, können Sie ja
gegebenenfalls die Einstellung des ucBorder-Controls zur
Entwicklungszeit ändern und das OCX für VB 5 neu kompilieren. Wenn
Sie in den Projekteigenschaften im Register "Komponente"
die Binär-Kompatibilität wählen, gibt es keinerlei Probleme
damit, da die Schnittstelle(n) des Controls nach außen hin von
dieser Maßnahme unberührt blieben.
|