12.9 Auswahllisten (ListBox)
Oft ist es erforderlich, dem Benutzer zur Laufzeit eine Auflistung von Elementen in Form von Zeichenfolgen anzubieten. Aus dieser Liste kann der Benutzer ein oder auch mehrere Elemente auswählen. .NET stellt zu diesem Zweck mehrere Steuerelemente zur Verfügung, von denen die drei wichtigsten
- ListBox
- CheckedListBox
- ComboBox
sind. In diesem Abschnitt werden wir uns zunächst der Klasse ListBox widmen, die beiden anderen werden danach erläutert.
12.9.1 Daten einer ListBox
Die von einer ListBox verwalteten Listeneinträge sind Elemente einer Auflistung vom Typ ListBox.ObjectCollection, auf die über die Eigenschaft Items zugegriffen werden kann. Wenn wir der Listbox ein Element hinzufügen wollen, ein bestimmtes Element aus ihr entfernen wollen oder nach einem bestimmten Element suchen, greifen wir auf die Methoden dieser Auflistung zurück.
Neue Listenelemente
Manchmal ist es möglich, eine ListBox bereits zur Entwicklungszeit vollständig zu füllen. Sie brauchen in diesem Fall im Eigenschaftsfenster nur die Eigenschaft Items zu markieren und können über die Schaltfläche in der Wertespalte einen Dialog öffnen, in dem Sie die Listenelemente der Reihe nach eintragen.
Meistens allerdings werden Sie Listen erst zur Laufzeit dynamisch füllen können, um sich unterschiedlichen Umgebungsbedingungen anzupassen. Als Mitglieder einer Collection werden Listenelemente über Indizes verwaltet. Das erste Listenelement hat, wie üblich, den Index 0. Drei Methoden ermöglichen es Ihnen, ein Element einer Listbox hinzuzufügen: Add, AddRange und Insert.
Betrachten wir zuerst die Add-Methode, deren Rückgabewert der Index ist, an dem das Element hinzugefügt worden ist. Das Element, das hinzugefügt werden soll, ist vom Typ Object und damit beliebig. Angezeigt wird der Rückgabewert der Methode ToString.
Im folgenden Codefragment werden mit der Add-Methode die drei Elemente Franz, Joseph und Uwe hinzugefügt.
Dim listBox1 As New ListBox(); listBox1.Items.Add("Franz") listBox1.Items.Add("Joseph") listBox1.Items.Add("Uwe")
Eine Alternative zum sich wiederholenden Add-Aufruf bietet die Methode AddRange, die ein Objekt-Array erwartet:
listBox1.Items.AddRange(New String() {"Franz", "Joseph", "Uwe"})
Soll ein neues Element nicht an das Ende angehängt werden, sondern eine bestimmte Position innerhalb aller Listenelemente einnehmen, verwenden Sie die Methode Insert. Dem ersten Parameter wird der gewünschte Index des hinzuzufügenden Elements übergeben, dem zweiten Parameter das Element. Die folgende Anweisung fügt Peter mit dem Index 1 an der zweiten Position der Listbox ein:
listBox1.Items.Insert(1, "Peter")
Wenn der Index größer ist als die Anzahl der Elemente in der Listbox, kommt es zu der Ausnahme ArgumentOutOfRangeException.
Listenelemente können mit Sorted auch alphabetisch sortiert werden. Sorted ist standardmäßig auf False eingestellt. Fügen Sie ein Element in eine sortierte Liste ein, wird das neue Element direkt einsortiert.
Hinzufügen vieler Listenelemente
Die Methode Add hat im Vergleich zur AddRange-Methode den Nachteil, dass die Listbox mit jedem hinzugefügten Listenelement neu aufgebaut wird. Handelt es sich um eine größere Elementanzahl, hat das einen spürbaren Leistungsverlust zur Folge. Besonders deutlich wird der Effekt in Kombination mit Sorted=True. Mit der Methode BeginUpdate lässt sich das Aktualisieren einer Listbox so lange unterdrücken, bis EndUpdate aufgerufen wird. Der Leistungsgewinn kann unter Umständen drastisch sein.
Die folgenden Anweisungen demonstrieren den Einsatz der beiden Methoden. Für die Dauer des Schleifendurchlaufs wird der Mauscursor als Sanduhr angezeigt, um zu signalisieren, dass die Anwendung eine länger andauernde Operation ausführt.
listBox1.Sorted = True listBox1.BeginUpdate() Me.Cursor = Cursors.WaitCursor For i As Integer = 0 To 999 listBox1.Items.Add(i) Next listBox1.EndUpdate() Me.Cursor = Cursors.Default
Den Effekt können Sie sehr gut beobachten, wenn Sie die Aufrufe von BeginUpdate und EndUpdate auskommentieren. Der Aufbau der Listbox benötigt dann ein Vielfaches der Zeit.
Löschen eines Elements
Jedes Element in einer Listbox kann durch den in der Listbox angezeigten Text und durch seinen Index beschrieben werden. Analog hat ListBox.ObjectCollection zwei Methoden, um ein bestimmtes Element aus der Auflistung zu entfernen: Remove und RemoveAt.
Remove erwartet als Argument die Referenz auf ein Objekt und wertet wiederum dessen ToString-Rückgabe aus; RemoveAt nimmt den Index entgegen. Wird ein Element mitten aus der Liste entfernt, verschieben sich alle Nachfolgeindizes, denn eine Auflistung kann niemals eine unbesetzte Position enthalten.
listBox1.Items.Remove("Joseph")
Clear löscht alle Listenelemente gleichzeitig.
Doppeleinträge vermeiden
Die Listenelemente einer Listbox sind nicht eindeutig – Sie können dasselbe Element auch mehrfach hinzufügen. Um das zu vermeiden, muss ein hinzuzufügendes Element zuerst daraufhin überprüft werden, ob es bereits eingetragen ist. Die Information darüber liefert die Methode Contains unter Angabe des Objekts. Der Rückgabewert ist vom Typ Boolean und True, wenn das im Parameter genannte Objekt in der Liste enthalten ist.
If Not listBox1.Items.Contains("Franz") Then listBox1.Items.Add("Franz")
Position eines Listenelements
Die Methode IndexOf() liefert die Position eines Elements in der Listbox.
Dim index As Integer = listBox1.Items.IndexOf("Marokko")
Ist das angegebene Element in der Liste nicht enthalten, ist der Rückgabewert -1.
12.9.2 Darstellung einer Listbox
Jetzt wissen Sie, wie die Elemente einer Listbox in der speziellen Auflistung ListBox.ObjectCollection verwaltet werden. Wir können uns nun den Eigenschaften der ListBox zuwenden, die die Darstellung zur Laufzeit kontrollieren.
Die Höhe eines Steuerelements kann grundsätzlich beliebig eingestellt werden. Bei einer ListBox kann das dazu führen, dass ein Listenelement nur teilweise angezeigt wird. Mit der Eigenschaft IntegralHeight wird das vermieden. Diese Eigenschaft ist auf True voreingestellt und bewirkt, dass sich die Höhe der Listbox automatisch so anpasst, dass alle Elemente vollständig angezeigt werden.
Läuft die Liste über und können nicht alle Elemente gleichzeitig angezeigt werden, wird zur Laufzeit automatisch eine vertikale Bildlaufleiste eingeblendet. Insbesondere bei Listboxen, die zur Laufzeit dynamisch gefüllt werden, möchten Sie vielleicht auch dann eine Bildlaufleiste anzeigen, wenn die Anzahl der Listenelemente das Steuerelement noch nicht vollständig ausfüllt. Stellen Sie dazu die Eigenschaft ScrollAlwaysVisible=True ein. Überschreitet die Breite des Listeneintrags die Breite der Listbox, ist der nicht sichtbare Teil rechts abgeschnitten. In diesem Fall kann mit der Eigenschaft HorizontalScrollbar eine horizontale Bildlaufleiste angezeigt werden.
Mehrspaltige Listboxen
Unabhängig davon, wie viele Elemente in der Auflistung enthalten sind, ist eine Listbox zunächst immer nur einspaltig. In Abbildung 12.12 enthält die Listbox alle auf dem aktuellen System installierten Schriftarten, die über die Liste InstalledFontCollection im Namensraum System.Drawing.Text bereitgestellt werden. Um die Listbox beim Laden des Formulars zu füllen, eignet sich das Load-Ereignis oder der Konstruktor der Form:
Private Sub Fonts_Load(ByVal sender As Object, ByVal e As EventArgs) _ Handles MyBase.Load Dim fonts As New System.Drawing.Text.InstalledFontCollection() Dim familie As FontFamily() = fonts.Families For Each f As FontFamily In familie Art.Items.Add(f.Name) Next ... End Sub
Die Eigenschaft Families eines InstalledFontCollection-Objekts liefert ein Array vom Typ FontFamily zurück. Dieser Typ definiert eine Schriftart und veröffentlicht die Eigenschaft Name, die den Bezeichner des Fonts liefert. In der Schleife werden die Namen aller Schriftarten mit der Add-Methode zur Items-Auflistung hinzugefügt.
Abbildung 12.12 Anzeige aller installierten Fonts in einer Listbox
Wenn Sie möchten, können Sie die Listenelemente auch auf mehrere Spalten aufteilen. Die Verteilung der Spalten erfolgt dabei so, dass keine vertikalen Bildlaufleisten mehr notwendig sind, sondern nur noch eine horizontale, die die Navigation zwischen den Spalten ermöglicht. Dazu muss die Eigenschaft MultiColumn=True gesetzt werden. Eine Listbox mit mehreren Spalten sehen Sie in Abbildung 12.13.
Abbildung 12.13 Mehrspaltige Anzeige in einer Listbox
Die Spaltenbreite können Sie mit ColumnWidth kontrollieren. Aber seien Sie hier vorsichtig. Wenn Sie nämlich eine zu geringe Breite wählen, wird der »überhängende« Teil von der Folgespalte überdeckt. Der Standardwert von 0 legt eine vordefinierte Breite fest, die allerdings nicht die Lesbarkeit aller Elemente sicherstellt.
12.9.3 Einfach- und Mehrfachauswahl
Standardmäßig kann in einer Listbox zur Laufzeit nur ein Eintrag ausgewählt werden. Soll der Anwender gleichzeitig mehrere Einträge markieren können, legen Sie die Eigenschaft SelectionMode der Listbox entsprechend fest. Diese Eigenschaft bezieht ihre Werte aus der gleichnamigen Enumeration, die die vier Mitglieder in Tabelle 12.8 hat.
Konstante | Selektion |
None |
Es kann kein Listenelement selektiert werden. |
One |
Nur ein Listenelement kann selektiert werden. |
MultiSimple |
Jede Auswahl eines Listenelements verändert die Selektion um genau ein Element. |
MultiExtended |
Durch Druck auf die -Taste oder die Umschalttaste ergänzen Sie eine Auswahl eines Listenelements um eine bereits vorhandene Selektion. |
Bei der Einstellung MultiSimple kann der Anwender mittels Mausklick in beliebiger Reihenfolge die Elemente auswählen, die dann invertiert dargestellt werden. Zudem kann er mit den Pfeiltasten durch die Liste navigieren und mit der Leertaste das fokussierte Element selektieren. Ein Mausklick oder das Drücken der Leertaste auf ein ausgewähltes Element hebt die Markierung des Elements wieder auf.
Die Auswahl MultiExtended ist dann vorteilhaft, wenn der Anwender einen Bereich aufeinanderfolgender Elemente auswählen soll. Wird ein Element mit der Maus ausgewählt und danach die Umschalttaste gedrückt, sind nach dem Klick auf ein zweites Listenelement alle Elemente zwischen diesen beiden automatisch selektiert. Ähnlich kann auch die Auswahl mit der Tastatur erweitert werden, indem man die Umschalttaste beim Drücken der Pfeiltasten gedrückt hält. Bei gleichzeitigem Druck auf die -Taste kann die Selektion um genau ein Element verändert werden. So können auch nicht zusammenhängende Bereiche selektiert werden.
12.9.4 Listboxen mit Einfachauswahl
Verwenden Sie eine Listbox, die nur die Auswahl eines Elements zulässt, gestaltet sich der Zugriff auf das entsprechende Element anders als bei einer Listbox mit Mehrfachauswahl. Betrachten wir zuerst Listboxen, deren Eigenschaft SelectionMode auf One eingestellt ist.
Am einfachsten ist es, die Eigenschaft Text der Listbox abzurufen, zum Beispiel:
MessageBox.Show(listBox1.Text)
Nahezu gleichwertig können Sie auch über SelectedItem auf das ausgewählte Listenelement zugreifen, allerdings ist der Rückgabewert dieser Eigenschaft nicht vom Typ String, sondern die Referenz auf das Objekt. Dies macht unter Umständen eine Konvertierung mit ToString() bei dessen Verwendung erforderlich.
MessageBox.Show(listBox1.SelectedItem.ToString())
Eine dritte Möglichkeit bietet sich mit der Eigenschaft Items, die die Referenz auf eine ListBox.ObjectCollection liefert. Die Auflistung stellt einen Indexer bereit, dem der Index des ausgewählten Elements übergeben wird. Weil der Indexer den Typ Object ausgibt, muss die Rückgabe gegebenenfalls in eine Zeichenfolge umgewandelt werden:
MessageBox.Show(listBox1.Items(12).ToString())
Die Indexangabe darf natürlich nicht statisch codiert werden, weil sie sich zur Laufzeit abhängig von der Wahl des Anwenders ändert. Hier hilft uns die Eigenschaft SelectedIndex der Listbox weiter, die den Index des ausgewählten Elements bereitstellt. Die Anweisung zur Auswertung des ausgewählten Elements über den Indexer der Auflistung muss daher so lauten:
MessageBox.Show(listBox1.Items(listBox1.SelectedIndex).ToString())
SelectedIndex ruft nicht nur den Index des ausgewählten Elements in einer Listbox ab, sondern kann ihn auch festlegen. Das ist sehr nützlich, denn standardmäßig wird nach dem Laden und Anzeigen eines Formulars kein Listenelement vorselektiert.
Das Ereignis SelectedIndexChanged
In einer Listbox führt sehr häufig das Anklicken eines Listenelements zur sofortigen Verarbeitung. Statt Click wird das Ereignis SelectedIndexChanged ausgelöst. Sehen wir uns das an einem Beispiel an, dessen Form zwei Listboxen enthält (siehe Abbildung 12.14).
Abbildung 12.14 Form des Beispiels »Einfachauswahl«
In der linken Listbox wird die Liste aller installierten Fonts angezeigt, in der rechten hat der Anwender die Auswahl der Schriftgröße. Die Änderung der Auswahl in einer der beiden Listboxen bewirkt in der Textbox die Anpassung der Zeichenfolge.
'...\WinControls\Selektionen\Einfachauswahl.vb |
Public Class Einfachauswahl Private Sub Laden(ByVal sender As Object, ByVal e As EventArgs) _ Handles MyBase.Load Dim fonts As New System.Drawing.Text.InstalledFontCollection() Dim familie As FontFamily() = fonts.Families ' Listbox mit den installierten Schriftarten füllen For Each f As FontFamily In familie.Where( _ Function(family) family.IsStyleAvailable(FontStyle.Regular)) Art.Items.Add(f.Name) Next ' Listbox mit einer Auswahl an Schriftgrößen füllen For Each no As Integer In Enumerable.Range(8, 17) Größe.Items.Add(no) Next ' Vorauswahl in den Listboxen Art.SelectedIndex = 0 Größe.SelectedIndex = Größe.Items.IndexOf(10) SelectedIndexChanged(Nothing, Nothing) End Sub Private Sub SelectedIndexChanged(sender As Object, e As EventArgs) _ Handles Art.SelectedIndexChanged, Größe.SelectedIndexChanged If Not Art.Text.Equals("") AndAlso Not Größe.Text.Equals("") Then _ Beispieltext.Font = New Font(Art.Text, Convert.ToSingle(Größe.Text)) End Sub End Class
Zuerst werden die beiden Listboxen im Load-Ereignishandler der Form gefüllt. Die Elemente, die in der Listbox Art angeboten werden, beziehen wir aus der Auflistung InstalledFontCollection. Anders als weiter oben gezeigt, sollen nur Schriften erscheinen, die auch »normal« (das heißt zum Beispiel nicht fett oder kursiv) verwendet werden können. Ohne diese Filterung kommt es zu einem Laufzeitfehler, wenn eine Schriftart gewählt wird, die keinen »normalen« Schriftschnitt unterstützt, wie zum Beispiel Monotype Corsiva. Damit nicht immer die gleiche Art einer If-Abfrage auftaucht, verwende ich die Methode Where zur Selektion. Die Methode IsStyleAvailable wird zur Prüfung verwendet.
familie.Where(Function(family) family.IsStyleAvailable(FontStyle.Regular))
Weil in einer Listbox nach dem Start der Anwendung zunächst kein Element vorselektiert ist, sollten wir das per Programmcode für beide Listboxen vorgeben:
Art.SelectedIndex = 0 Größe.SelectedIndex = Größe.Items.IndexOf(10)
Die Ereignisse SelectedIndexChanged der beiden Listboxen sind mit dem gemeinsamen Handler SelectedIndexChanged verknüpft, der die Schrift in der Textbox an die Auswahl anpasst.
Bitte beachten Sie die If-Abfrage in der Methode, bevor der neue Font zugewiesen wird. Vor der Spezifikation einer konkreten Selektion der Listboxen haben die Text-Eigenschaften den Wert "", der weder ein gültiger Font noch eine gültige Größe ist. Ohne diesen Test kommt es zu einem Laufzeitfehler. Alternativ können Sie statt der Handles-Klausel des Ereignishandlers die Registrierung mit AddHandler vornehmen, wenn diese nach der Festlegung einer Selektion erfolgt.
Im Ereignishandler müssen wir nur die Font-Klasse mit einem passenden Konstruktor instanziieren. Geeignet ist in unserem Fall der Konstruktor, der die Zeichenfolge der Schriftart und die Größe der Schrift erwartet. Das neue Font-Objekt wird der Eigenschaft Font des Textfeldes zugewiesen. Dabei müssen wir allerdings noch den Übergabewert an den zweiten Parameter in den erwarteten Typ Single konvertieren:
Beispieltext.Font = New Font(Art.Text, Convert.ToSingle(Größe.Text))
Die letzte Anweisung im Load-Ereignishandler ist der Aufruf der Methode SelectedIndexChanged. Damit stellen wir sicher, dass nach dem Starten die Schriftart in der Textbox der Voreinstellung in den Listboxen entspricht.
12.9.5 Listboxen mit Mehrfachauswahl
Mit der Eigenschaft SelectionMode kann die einfache oder erweiterte Mehrfachauswahl zugelassen werden. Listenelemente mit Mehrfachauswahl verwalten die ausgewählten Listenelemente in zwei Auflistungen:
- SelectedObjectCollection
- SelectedIndexCollection.
Der Zugriff erfolgt über die Eigenschaften SelectedIndices und SelectedItems. Beide enthalten die ausgewählten Elemente der Listbox, zu denen sie eine Beziehung entweder über die Referenz oder über den Index herstellen.
Angenommen, Peter, Uwe und Willi bilden in dieser Reihenfolge die Liste der Listbox. Sind Uwe und Willi ausgewählt, enthält SelectedObjectCollection im ihrem ersten, nullindizierten Element den Verweis auf Uwe und im zweiten, mit 1 indizierten Element den Verweis auf Willi. Analog speichert SelectedIndexCollection die Indizes 0 und 1.
Beide Auflistungen verfügen über die üblichen Eigenschaften und Methoden von Auflistungen, beispielsweise Count, Contains, IndexOf, sowie über einen Indexer, um auf ein bestimmtes Element zuzugreifen.
Sehen wir uns an einem Beispiel an, wie ein Listenelement mit Mehrfachauswahl im Programmcode behandelt wird. Die in Abbildung 12.15 gezeigte Form enthält dazu zwei Listboxen, die beide eine Mehrfachauswahl mit SelectionMode=MultiSimple zulassen. In der linken wird nach dem Start der Anwendung eine Namensliste angezeigt. Der Benutzer kann mehrere Einträge in der linken Listbox auswählen und durch Klicken auf den oberen Button die ausgewählten Einträge in die rechte Listbox verschieben. Die markierten und verschobenen Einträge müssen danach in der Ursprungslistbox gelöscht werden. Der umgekehrte Vorgang, das Verschieben aus der rechten in die linke Listbox, verläuft analog.
Abbildung 12.15 Form des Beispiels »Mehrfachauswahl«
'...\WinControls\Selektionen\Mehrfachauswahl.vb |
Public Class Mehrfachauswahl Private Sub Laden(ByVal sender As Object, ByVal e As EventArgs) _ Handles MyBase.Load Links.Items.AddRange(New String() {"Peter", "Uwe", _ "Michael", "Reiner", "Brigitte", "Thea", "Jackie"}) End Sub Private Sub Verschieben(ByVal von As ListBox, ByVal nach As ListBox) Dim elems(von.SelectedItems.Count – 1) As String von.SelectedItems.CopyTo(elems, 0) nach.Items.AddRange(elems) ' ausgewählte Elemente in Quelle löschen For i As Integer = von.SelectedItems.Count – 1 To 0 Step –1 von.Items.RemoveAt(von.SelectedIndices(i)) Next End Sub Private Sub NachRechts_Click(sender As Object, e As EventArgs) _ Handles NachRechts.Click Verschieben(Links, Rechts) End Sub Private Sub NachLinks_Click(sender As Object, e As EventArgs) _ Handles NachLinks.Click Verschieben(Rechts, Links) End Sub End Class
Im Ereignishandler des Load-Ereignisses der Form wird die linke Listbox gefüllt. Die Ereignishandler zu den Click-Ereignissen der Schaltflächen gleichen sich bis auf die Bezeichner, sodass wir die Funktionalität in der Methode Verschieben() zusammenfassen.
Die Ziel-Listbox soll mit der AddRange-Methode gefüllt werden. AddRange verlangt als Übergabeargument ein Objekt-Array, das wir uns vorher besorgen müssen. Deshalb deklarieren wir zuerst ein Array und initialisieren dieses mit einer Kapazität, die es erlaubt, es mit den ausgewählten Listenelementen aus der Ausgangslistbox füllen zu können. Die Array-Größe resultiert aus der Anzahl der ausgewählten Elemente, die in der Eigenschaft SelectedItems vom Typ SelectedObjectCollection gespeichert ist. Die Count-Eigenschaft der Klasse liefert die Anzahl.
Dim elems(von.SelectedItems.Count – 1) As String
Mit
von.SelectedItems.CopyTo(elems, 0)
kopieren wir den Inhalt der Auflistung SelectedObjectCollection in das zuvor deklarierte Array elems, beginnend beim Index 0. Dieses Array übergeben wir der AddRange-Methode.
Nun müssen wir die ausgewählten und bereits kopierten Listenelemente in der Listbox auch löschen. SelectedIndices der Quell-Listbox liefert uns als Rückgabewert die Indizes der ausgewählten Elemente. Diesen Rückgabewert übergeben wir der Methode RemoveAt der Listbox, die in einer Schleife für jedes markierte Listenelement aufgerufen wird:
For i As Integer = von.SelectedItems.Count – 1 To 0 Step –1 von.Items.RemoveAt(von.SelectedIndices(i)) Next
Achten Sie darauf, die Schleife ausgehend vom höchsten Zählerwert (der der Anzahl der ausgewählten Elemente minus 1 entspricht) bis 0 zu durchlaufen. Damit wird sichergestellt, dass zuerst das markierte Element mit dem höchsten Index aus der Listbox gelöscht wird und erst zum Schluss das mit dem kleinsten Index. Sie vermeiden so Fehler, die sich aus der Verschiebung der Indizes ergeben, denn auch die Listbox lässt keine unbesetzten Indizes zu und verschiebt alle Nachfolgeelemente um eine Position, um eine entstandene Lücke zu schließen. Die Übergabe des Schleifenzählers an SelectedIndices setzt aber eine konstante Zuordnung voraus, um einen logischen Fehler zu vermeiden.
12.9.6 Benutzerdefinierte Sortierung
Wenn Sie die Methode Sort auf die Listenelemente einer Listbox anwenden, werden die sichtbaren Einträge alphabetisch sortiert. Eine Alternative bietet sich nicht an, da die Methode keine entsprechenden Überladungen hat. Manchmal ist es jedoch erforderlich, eine andere Sortierreihenfolge festzulegen.
Angenommen, in einem ListBox-Objekt sollen Objekte vom Typ Person angezeigt werden.
Public Class Person Public Zuname As String Public Vorname As String Public Alter As Integer Public Sub New(zuname As String, vorname As String, alter As Integer) Me.Zuname = zuname Me.Vorname = vorname Me.Alter = alter End Sub Public Overrides Function ToString() As String Return Me.Zuname & " / " & Me.Vorname & " / " & Me.Alter End Function End Class
In der Klasse sind drei Felder öffentlich deklariert. Außerdem ist die Methode ToString() überschrieben, weil die Listbox bei der Übergabe von Referenzen immer den Rückgabewert von ToString() anzeigt. Die Überschreibung stellt sicher, dass zuerst der Zuname, dann der Vorname und zum Schluss das Alter für jeden Eintrag angezeigt werden.
Nun sollen die Elemente in der Listbox nach einem der drei Felder sortiert werden. Die Sorted-Eigenschaft der Listbox leistet das nicht. Wir müssen einen anderen Weg gehen. Dabei hilft uns die überladene Methode Sort der ArrayList weiter, die uns die Möglichkeit gibt, eine Vergleichsklassenreferenz vom Typ IComparer zu übergeben.
Public Overridable Sub Sort(ByVal comparer As IComparer) 'Klasse ArrayList
Eine Vergleichsklasse bereitzustellen, ist kein großes Problem. Eine Hürde ist vielmehr, dass die Methode Sort auf die Referenz einer ArrayList aufgerufen wird, wir jedoch eine Listbox benutzerdefiniert sortieren wollen. Auch über diese Schwierigkeit hilft uns die Klasse ArrayList hinweg: Sie veröffentlicht die statische Methode Adapter, der die Referenz auf ein IList-Objekt übergeben wird.
Public Shared Function Adapter(ByVal list As IList) As ArrayList
Der Zufall will es so, dass die von der Eigenschaft Items zurückgelieferte Referenz vom Typ ListBox.ObjectCollection genau diese Schnittstelle implementiert. Damit hätten wir alle notwendigen Informationen gesammelt, die uns in die Lage versetzen, eine benutzerdefinierte Sortierung der Listboxelemente anzubieten. Das folgende Beispielprogramm zeigt den Programmcode. Die Form in Abbildung 12.16 enthält neben einer Listbox auch drei Schaltflächen. Je nachdem, welche Schaltfläche der Benutzer anklickt, wird die Ausgabe in der Listbox nach einem der drei Felder sortiert.
Abbildung 12.16 Form des Beispiels »Sortierung«
'...\WinControls\Selektionen\Sortierung.vb |
Public Class Sortierung Public Class Person ... End Class Private sortiert As ArrayList Private Class Sortierer : Implements IComparer Private feld As System.Reflection.FieldInfo Sub New(ByVal feld As String) Me.feld = GetType(Person).GetField(feld) End Sub Public Function Compare(x As Object, y As Object) As Integer _ Implements System.Collections.IComparer.Compare Return feld.GetValue(x).CompareTo(feld.GetValue(y)) End Function End Class Private Sub Laden(ByVal sender As Object, ByVal e As EventArgs) _ Handles MyBase.Load Dim pers As Person() = New Person() { _ New Person("Müller", "Peter", 30), _ New Person("Fischer", "Helmut", 56), _ New Person("Popalowski", "Fred", 22), _ New Person("Heinrich", "Walter", 29), _ New Person("Meier", "Uwe", 12) _ } ' das Array der Listbox übergeben Personen.Items.AddRange(pers) sortiert = ArrayList.Adapter(Personen.Items) sortiert.Sort(New Sortierer("Zuname")) End Sub Private Sub Sortieren(ByVal sender As Object, ByVal e As EventArgs) _ Handles Zuname.Click, Vorname.Click, Alter.Click sortiert.Sort(New Sortierer(CType(sender, Button).Name)) End Sub End Class
12.9.7 Auswahlliste mit Datenquelle (DataSource)
Die Add-Methode des Objekts ObjectCollection fügt Listenelemente hinzu. Alternativ weisen Sie der Eigenschaft DataSource eine Referenz auf die Daten der Listbox zu. Eine Bedingung dafür ist, dass das Objekt die Schnittstelle IList implementiert. Üblicherweise wird diese Eigenschaft dazu benutzt, um beispielsweise ein ADO.NET-Objekt vom Typ DataSet zu befüllen. Wir werden uns ADO.NET an späterer Stelle in diesem Buch widmen. Die Vorgehensweise ist bei einem ArrayList-Objekts ganz analog.
Haben Sie die Daten der Listbox mit DataSource festgelegt, müssen Sie noch deren Verwendung festlegen:
- DisplayMember spezifiziert den Namen der Eigenschaft, die angezeigt wird.
- DisplayValue spezifiziert den Namen der Eigenschaft, auf den sich SelectedValue bezieht.
Machen wir uns das an einem konkreten Beispiel deutlich:
Public Class Wohnung Private flaeche As Double Private preis As Single Private zimmeranzahl As Integer Public Sub New(flaeche As Double, preis As Single, anzahl As Integer) Me.flaeche = flaeche Me.preis = preis zimmeranzahl = anzahl End Sub Public ReadOnly Property Wohnflaeche() As Double Get Return Wohnflaeche End Get End Property Public ReadOnly Property Miete() As Single Get Return preis End Get End Property Public ReadOnly Property Bezeichnung() As String Get Return zimmeranzahl.ToString() & "-Zimmer-Wohnung" End Get End Property End Class
In einer Listbox sollen die Bezeichnungen der Wohnungen angezeigt werden, in zwei Textboxen sowohl der Mietpreis als auch die Wohnfläche des ausgewählten Listenelements (siehe Abbildung 12.17). Wir verwenden die drei Eigenschaften DataSource, DisplayMember und ValueMember, um die Daten der Listbox festzulegen.
Abbildung 12.17 Form des Beispiels »Datenquelle«
'...\WinControls\Selektionen\Datenquelle.vb |
Public Class Datenquelle Public Class Wohnung ... End Class Private Wohnungen As New ArrayList() Private Sub Laden(sender As Object, e As EventArgs) Handles MyBase.Load Wohnungen.Add(New Wohnung(25, 300, 1)) Wohnungen.Add(New Wohnung(54, 470, 2)) Wohnungen.Add(New Wohnung(87, 729, 4)) Wohnungen.Add(New Wohnung(60, 650, 2)) Wohnungen.Add(New Wohnung(75, 680, 3)) Wohnungsliste.DataSource = Wohnungen Wohnungsliste.DisplayMember = "Bezeichnung" Wohnungsliste.ValueMember = "Miete" Wohnungsliste.SelectedIndex = 1 End Sub Private Sub SelectedIndexChanged(sender As Object, e As EventArgs) _ Handles Wohnungsliste.SelectedIndexChanged Miete.Text = Wohnungsliste.SelectedValue.ToString() Fläche.Text = _ CType(Wohnungsliste.SelectedItem, Wohnung).Wohnfläche.ToString() End Sub End Class
DataSource erwartet die Referenz auf ein Objekt, das IList implementiert. Hier ist es ein ArrayList-Objekt mit der Bezeichnung Wohnungen, dem mehrere Wohnung-Objekte hinzugefügt werden. Anschließend wird das ArrayList-Objekt der DataSource-Eigenschaft zugewiesen. Im ListBox-Steuerelement sollen die Inhalte der Eigenschaft Bezeichnung angezeigt werden. Das erreichen wir mit folgender Zuweisung:
Wohnungsliste.DisplayMember = "Bezeichnung"
Als »Wert« wählen wir willkürlich Miete und weisen dessen Name der Eigenschaft ValueMember zu. Die Spezifikation von ValueMember ist optional.
Wohnungsliste.ValueMember = "Miete"
Wählt der Anwender ein Listenelement aus, wird das Ereignis SelectedIndexChanged ausgelöst. In der Textbox Miete zeigen wir den Mietpreis des gewählten Listenelements an, indem wir die Eigenschaft SelectedValue auswerten, die uns den Wert der unter ValueMember genannten Eigenschaft liefert. Die Wohnfläche in der Textbox Fläche wird durch Auswertung von SelectedItem mit anschließender Konvertierung in den Typ Wohnung ermittelt.
Beachten Sie bitte, dass eine Listbox nicht mit Sorted sortiert werden kann, wenn die Eigenschaft DataSource zum Füllen benutzt wird.
Ihre Meinung
Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.