Probleme mit ListBox

  • VB.NET

Es gibt 23 Antworten in diesem Thema. Der letzte Beitrag () ist von VaporiZed.

    Probleme mit ListBox

    Hallo allerseits und einen guten Start ins neue Jahr! :)

    Ich habe zwei Probleme mit einer ListBox, die glaube ich zusammenhängen:

    (1) Bei meiner ListBox wird beim Laden der Form automatisch das oberste Item ausgewählt und damit das SelectedIndexChanged-Ereignis ausgelöst. Das will ich nicht haben. Gibt es eine Eigenschaft die das verhindert, ich hab keine gefunden...?

    (2) Um dem Problem vorläufig aus dem Weg zu gehen, hab ich meinen Code aus dem SelectedIndexChanged erstmal in das MouseClick-Ereignis gepackt. Ich will Folgendes erreichen:
    Bei der Auswahl eines Items in lstAlben soll meine "Datenbank" (dass das nicht wirklich eine ist weiss ich, ich nenn das jetzt mal so) nach allen Titeln dieses Albums durchsucht werden und diese in lstMusiktitel angezeigt werden. Funktioniert soweit, bloss das Problem ist es wird jeweils immer nur der erste Titel des Albums angezeigt. Kann das sein, dass nach dem ersten Durchlauf der For...Each-Schleife sich das lstAlben.SelectedItem ändert, also so dass dann letztendlich nichts mehr ausgewählt ist? Das wär so meine Idee sonst kommt mir an dem Code nichts spanisch vor...

    HIer mein Code (in Content hab ich vorher die Daten in einer separaten Prozedur geladen, das funktioniert aber so weit):

    VB.NET-Quellcode

    1. Public Class Form1
    2. Public Content As List(Of ClassMP3Datei)
    3. Public mp3 As ClassMP3Datei
    4. Private Sub lstAlben_MouseClick(sender As Object, e As MouseEventArgs) Handles lstAlben.MouseClick
    5. 'ListenLeeren()
    6. listAlleMusiktitel.Clear()
    7. listAlleSendernamen.Clear()
    8. For Each mp3 In Content
    9. If mp3.Album Is lstAlben.SelectedItem() Then
    10. listAlleMusiktitel.Add(mp3.Musiktitel)
    11. End If
    12. Next
    13. ListenFüllen()
    14. End Sub
    15. Private Sub ListenFüllen()
    16. listAlleDateinamen = listAlleDateinamen.Distinct().ToList()
    17. listAlleInterpreten = listAlleInterpreten.Distinct().ToList()
    18. listAlleAlben = listAlleAlben.Distinct().ToList()
    19. listAlleMusiktitel = listAlleMusiktitel.Distinct().ToList()
    20. listAlleSendernamen = listAlleSendernamen.Distinct().ToList()
    21. listAlleURLs = listAlleURLs.Distinct().ToList()
    22. lstInterpreten.DataSource = listAlleInterpreten
    23. lstAlben.DataSource = listAlleAlben
    24. lstMusiktitel.DataSource = listAlleMusiktitel
    25. lstSender.DataSource = listAlleSendernamen
    26. End Sub
    27. End Class

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „kafffee“ ()

    kafffee schrieb:

    (1) Bei meiner ListBox wird beim Laden der Form automatisch das oberste Item ausgewählt und damit das SelectedIndexChanged-Ereignis ausgelöst. Das will ich nicht haben.
    Wieso nicht?
    Bei Databinding ist das halt so: Wenn Daten da sind, dann ist auch ein Datensatz ausgewählt.
    Das gewährt eine konsistente Präsentation.

    Zum andern Problem einfach mal debuggen. Kannst ja im Einzelschritt durchsteppen und gucken, ob was wo geadded wird, und warum und wann es wieder verschwindet.
    Debuggen kannste? Also Haltepunkt, Lokal-Fenster, Callstack etc...
    Ansonsten oder sowieso gugge: VisualStudio richtig nutzen (Google ist nicht deine Mami)

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ErfinderDesRades“ ()

    Beim Laden der Form werden in separaten Listboxen alle (!) Interpreten, Alben und Musiktitel angezeigt. Das soll auch so bleiben bis der Benutzer einen Interpret oder Album aus der jeweiligen Liste auswählt. Denn dann sollen wie gesagt nur (!) die Alben des ausgewählten (!) Interpreten bzw. nur die Musiktitel des ausgewählten Albums angezeigt werden. Ich hab das bis jetzt umgangen indem ich wie gesagt auf das Click-Ereignis umgesattelt hab, um die Alben bzw. Musiktitel dann rauszusuchen. Klappt auch, aber hat einen kleinen Schönheitsfehler: Wenn ich ein Item auswähle, bleibt dieses nicht markiert, sondern die Markierung springt dann immer wieder auf das erste Item zurück. Das verwirrt den Benutzer nur unnötig...
    Bei meiner ListBox wird beim Laden der Form automatisch das oberste Item ausgewählt und damit das SelectedIndexChanged-Ereignis ausgelöst. Das will ich nicht haben.
    Schnellschuss von mir, da stark erkältet: Benutze drüber eine Boolean-Variable, die beim Programmstart (Laden der Form) False ist und erst später True wird.
    An die Neulinge: Nutzt Option Strict On und Option Infer Off. Dadurch kommt ihr mit Datentypumwandlungen nicht durcheinander und der Code verbessert sich um Einiges! Solche Fehler à la Dim Beispiel As Integer = "123" können nicht mehr passieren.

    kafffee schrieb:

    Wenn ich ein Item auswähle, bleibt dieses nicht markiert, sondern die Markierung springt dann immer wieder auf das erste Item zurück.
    Vermutlich weist du eine neue DataSource zu.
    Dann ist natürlich die Selection der vorherigen Datasource flöten.
    Aber sowas kann man ja debuggen, wo das genau passiert.
    Hab ich grad auch bemerkt :)

    Also Problem 1 wäre behoben.

    Zu Problem 2:

    Jetzt wird nicht mehr nur der erste Titel angezeigt, sondern es tut sich gar nichts und die Anzeige aller (!) Musiktitel bleibt bestehen. Mein Code sieht jetzt so aus:

    VB.NET-Quellcode

    1. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    2. FormFrischGeladen = True
    3. End Sub
    4. Private Sub lstAlben_SelectedIndexChanged(sender As Object, e As EventArgs) Handles lstAlben.SelectedIndexChanged
    5. If FormFrischGeladen = False Then
    6. listAlleMusiktitel.Clear()
    7. For Each mp3 In Content
    8. If mp3.Album Is lstAlben.SelectedItem() Then
    9. listAlleMusiktitel.Add(mp3.Musiktitel)
    10. End If
    11. Next
    12. lstMusiktitel.DataSource = listAlleMusiktitel
    13. 'ListenFüllen()
    14. Else
    15. FormFrischGeladen = False
    16. End If
    17. End Sub

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „kafffee“ ()

    Kleiner Tipp für die Übersicht:

    VB.NET-Quellcode

    1. Private Sub lstAlben_SelectedIndexChanged(sender As Object, e As EventArgs) Handles lstAlben.SelectedIndexChanged
    2. If FormFrischGeladen Then FormFrischGeladen = False: Return
    3. ' restlicher Code


    ##########

    @kafffee
    Noch was:

    VB.NET-Quellcode

    1. For Each mp3 In Content.Where(Function(x) x.Album Is lstAlben.SelectedItem())
    2. listAlleMusiktitel.Add(mp3.Musiktitel)
    3. Next

    Man könnte auch die For-Schleife in eine ForEach-LINQ-Extension umwandeln.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „VaporiZed“ ()

    @VaporiZed

    VaporiZed schrieb:

    Man könnte auch die For-Schleife in eine ForEach-LINQ-Extension umwandeln.

    Ich nehme an der Unterschied ist, sich überflüssigen Code zu ersparen?

    @ErfinderDesRades

    kafffee schrieb:

    Jetzt wird nicht mehr nur der erste Titel angezeigt, sondern es tut sich gar nichts und die Anzeige aller (!) Musiktitel bleibt bestehen. Mein Code sieht jetzt so aus:


    OK. Ich hab jetzt zwei Stunden lang alles Mögliche ausprobiert und bin dann auf die Idee gekommen, die Listen und das DataSource aussen vor zu lassen und direkt mit den ListBoxes zu arbeiten, was ja eh unnötig ist, da in diesem speziellen Fall keine doppelten Einträge vorhanden sein werden, weshalb ich ja ursprünglich den Umweg über die Listen gemacht habe. (Es wird wohl keine Interpreten geben, die zwei gleichnamige Alben veröffentlichen, und wenn ja dann nehm ich das in Kauf).
    Jetzt bin ich aber an einem Punkt angekommen, wo ich wirklich nicht mehr weiter weiss, ich hab mir den Code des kompletten Programms (und nicht nur die betroffene Prozedur) abermals und abermals durchgelesen...:
    Es wird immer nur ein Eintrag gefunden. Zum Vergleich (mit Post#1): An anderer Stelle des Programms benutze ich eine Textbox zum Suchen nach Interpreten, Alben und Musiktiteln, das funktioniert es tadellos...:

    VB.NET-Quellcode

    1. Private Sub SearchButton_Click(sender As Object, e As EventArgs) Handles Button1.Click
    2. ListenLeeren()
    3. For Each mp3 In Content
    4. If mp3.Interpret = TextBox1.Text Then
    5. listAlleInterpreten.Add(mp3.Interpret)
    6. listAlleAlben.Add(mp3.Album)
    7. listAlleMusiktitel.Add(mp3.Musiktitel)
    8. End If
    9. Next
    10. [usw...]
    11. ListenFüllen()
    12. End Sub


    Vorher hab ich mp3 und Content in einer separaten Prozedur wie folgt "beladen":

    VB.NET-Quellcode

    1. For i = 0 To listAlleDateinamen.Count() - 1
    2. mp3 = New ClassMP3Datei(listAlleDateinamen(i), listAlleInterpreten(i), listAlleAlben(i), listAlleMusiktitel(i))
    3. Content.Add(mp3)
    4. Next


    Dabei sind die beiden wie folgt deklariert:

    VB.NET-Quellcode

    1. Public Class Form1
    2. Public Content As List(Of ClassMP3Datei)
    3. Public mp3 As ClassMP3Datei
    4. [...]


    Wär wirklich gut wenn mir jemand den Fehler erklären würde, ich bin mittlerweile echt verzweifelt...

    kafffee schrieb:

    Ich nehme an der Unterschied ist, sich überflüssigen Code zu ersparen?
    Überflüssig nicht, aber kürzer. Nur ist für manche eine Code-Kürzung auch mit Verständnisverschlechterung verbunden.
    Aber so könnte es eben auch aussehen, auch wenn es ohne ForEach-Extension ist (ist zwar auch möglich, aber unnötig und macht das Ganze zumindest hier wieder nur komplizierter als sinnvoll):

    VB.NET-Quellcode

    1. listAlleMusiktitel.AddRange(Content.Where(Function(x) x.Album Is lstAlben.SelectedItem()))

    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    kafffee schrieb:

    Es wird immer nur ein Eintrag gefunden.
    Sowas liegt manchmal auch daran, dass nur ein Eintrag da ist.
    Aber das kann man ja debuggen.
    Bzw du brauchst alle Musiktitel ja nur anzugucken.
    Ah - vlt. ist der Fehler dieser:

    VB.NET-Quellcode

    1. 'statt
    2. If mp3.Album Is lstAlben.SelectedItem() Then
    3. 'muss heissen
    4. If mp3.Album = lstAlben.SelectedItem.ToString Then
    vb.net hat ca. 200 Schlüsselworte und vlt. 20 Operatoren.
    Die muss man alle lernen (das klingt viel ist es aber nicht).
    Is und = gehören zu den wichtigsten.
    Und haben ausserdem kontextabhängig auch noch unterschiedliche Bedeutung.
    Vb.net-Schlüsselworte



    Und bitte: Mit Listen arbeiten, nicht mit Listboxen
    Ein Haufen Listen ist ein schlechtes Datenmodell, ein Haufen Listboxen ist aber gar keins.

    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „ErfinderDesRades“ ()

    @ErfinderDesRades

    ErfinderDesRades schrieb:

    'statt
    If mp3.Album Is lstAlben.SelectedItem() Then
    'muss heissen
    If mp3.Album = lstAlben.SelectedItem.ToString Then


    Alles klar es funktioniert! Da wär ich wahrscheinlich wie immer nie drauf gekommen... Das hätte mich jetzt echt geärgert, wenn mein Projekt an so einem Detail scheitert. Da denkt man, man versteht die Funktion eines Schlüsselworts...

    Hab übrigens auch wieder auf Listen umgesattelt...
    man kanns übrigens auch streng typisiert ausdrücken, also ohne die .ToString()-Typumwandlung:

    VB.NET-Quellcode

    1. If mp3.Album = listAlleAlben(lstAlben.SelectedIndex) Then
    So arbeitest du mit den Daten (aus der Datasource).
    statt mit einem Item (was ja alles sein kann) aussm Control.
    ZB Wenn du mal andere Objekte als dumme Strings in die Listbox tust, und Listbox.DataMember, .DisplayMember verwendest wird das noch wichtiger.
    Ich hatte das Thema zwar schon geschlossen, jetzt bin ich aber doch noch auf ein (hoffentlich) sehr kleines Problem gestossen, das auch hier reinpasst:

    Ich möchte mit einem Klick auf den Button alle selektierten Titel der lstMuisiktitel zu einer lstPlaylist zufügen, die zugehörigen Verzeichnisse und Dateinamen in einer HilfstlisteDateinamen As New List (Of String) hinterlegen, um später bei einem Druck auf den PlayButton die richtige Datei zum Abspielen zu finden. ListBox1 ist nur zu Testzwecken da. Wie auch immer, beim ersten Klick auf den btnZuPlaylistZufügen funktioniert das einwandfrei. Bei jedem weiteren Klick wird zwar die lstPlaylist wieder erweitert, jedoch bleiben bei der ListBox1 zwar die bestehenden Items vorhanden, es werden jedoch keine weiteren zugefügt und ich komm nicht drauf woran das liegt.

    Hier mein Code:

    VB.NET-Quellcode

    1. Private Sub btnZuPlaylistZufügen_Click(sender As Object, e As EventArgs) Handles btnZuPlaylistZufügen.Click
    2. For Each SelectedItem In lstMusiktitel.SelectedItems
    3. lstPlaylist.Items.Add(SelectedItem)
    4. For Each mp3 In Content
    5. If mp3.Musiktitel = SelectedItem.ToString Then
    6. HilfslisteDateinamen.Add(mp3.Dateiname)
    7. End If
    8. Next
    9. Next
    10. ListBox1.DataSource = HilfslisteDateinamen
    11. End Sub
    ja, das liegt daran, dass dumme List(Of String) Databinding nur mangelhaft unterstützen.
    Wenn Listbox1.DataSource vorher HilfslisteDateinamen war, und du weist ihr dieselbe Liste nochmal zu - da ändert sich nix.

    2 Möglichkeiten: Du könntest bischen umbauen und für HilfslisteDateinamen eine BindingList(Of String) nehmen statt der List(Of String).
    Dann brauchste die DataSource auch nicht jedesmal neu zu setzen, sondern nurnoch initial beim Programmstart.
    Wenn Item hinzukommen merkt die BindingList(Of String) das und meldet es automatisch an die Listbox.
    So hast du schoma ein bischen mehr Databinding, wenn auch längst noch nicht das volle Programm

    Die andere Möglichkeit lautet weiterso:
    mach einfach

    VB.NET-Quellcode

    1. ListBox1.DataSource = HilfslisteDateinamen.ToList
    Das kopiert HilfslisteDateinamen um in eine neue Liste, somit ist es eine andere DataSource, und Listbox1 wird sich bemüssigt sehen, alle Elemente sich nochmal reinzuholen.
    @ErfinderDesRades

    D.h. dritte Möglichkeit? :

    Ich lass es einfach so, die Listbox 1 ist sowieso nur zum Testen und überprüfen da und die kompletten Daten sind "eigentlich" in der HilfslisteDateinamen enthalten und werden bloss nicht in der Listbox 1 angezeigt?

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „kafffee“ ()

    ja, das wäre dann richtiges Databinding.

    Also bei "richtiges" Databinding kann man zwei Moden untersheiden:
    Einmal aktualisieren sich die Properties einzelner Elemente. Also ändere den Namen eines Mp3Objekts per code, dann zeigt die Listbox das auch an.
    Zum andern aktualisieren sich die Listen wenn zugefügt oder entfernt wird. Das hätte jetzt die BindingList leisten können.

    Und beides ist bidirektional, also was du per Code änderst wird auch geändert in den Controls angezeigt, und was du als User ins Control eingibst wandert direkt in die Daten (und kommt sogar bei anderen Controls wieder raus).

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ErfinderDesRades“ ()

    Wow sehr cool. Muss ich da noch irgendwas machen oder langt ein simples Binden der Liste an eine Listbox wie ich es bisher gemacht habe?

    Noch eine speziellere Situation:
    Ich habe eine Liste mit den Dateinamen der Musiktitel und eine Listbox mit den Musiktiteln.

    Wenn ich nun die Reihenfolge der Items der Listbox ändere soll die Reihenfolge der Einträge der Liste analog dazu geändert werden...

    Kann mir da DataBinding auch in irgendeiner Weise unter die Arme greifen?

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „kafffee“ ()