Announcement

Collapse
No announcement yet.

SQL-Daten in Listbox/Listview

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • SQL-Daten in Listbox/Listview

    Hallo Zusammen!

    Ich beiss mir grad die Zähne an folgedem aus:

    Habe eine Tabelle mit Address-Daten in einer SQL-DB. Diese Daten sollen nun in einer Listbox oder Listview (je nach dem was besser geeignet ist) zur Anzeige gebracht werden.
    Name, Strasse, Ort usw. jeweils in einer Spalte; pro Datensatz eine Zeile. Wenn der Datensatz angelickt wird, soll die ganze Zeile unterlegt werden (nicht nur der Spalteneintrag).
    Ich hock da jetzt schon zwei Tage dran und bekomms nicht hin und mittlerweile hab ich wahrscheinlich auch das berüchtigte Brett vor dem Kopf.

    Über Tips oder sogar ein Beispiel-Code-Schnipsel wär ich sehr dankbar.

    Vielen Dank!
    Syn

  • #2
    Die Daten aus der SQL-Datenbank auszulesen kann etwas schwieriger werden, da gibt es aber bestimmt frei zugängliche Bibliotheken, die Du anwenden kannst, je nachdem welche Datenbank Du verwendest. Da kann ich Dir leider nicht weiterhelfen.

    Ansonsten: ListView ist meiner Meinung nach besser, wenn Du mehr als nur eine Information pro Datensatz ausgeben willst.

    Die Daten in die Listview einzutragen ist einfacher:
    1. Stelle die View-Eigenschaft auf Details
    2. Deklariere in der Columns Eigenschaft so viele Einträge, wie Du als Spalten aus der Datenbank auslesen/anzeigen willst
    3. Setze FullRowSelect auf True
    4. Setze HideSelection auf False
    5. Fülle die ListView mit entsprechendem Code:
      Code:
      For Each DBItem In Abfrageergebnis
         Dim itm As ListViewItem = listview1.Items.Add(DBItem.Spalte1)
         itm.Tag = DBItem.ID
         itm.SubItems.Add(DBItem.Spalte2)
         itm.SubItems.Add(DBItem.Spalte3)
      ...
      Next
      den Tag zu setzen ist nicht unbedingt notwendig, erleichtert aber die Zuordnung, welcher DB-Eintrag vom User ausgewählt wurde.


    FullRowSelect ist dafür, die gesamte Zeile zu markieren, HideSelection=False markiert den gewählten Eintrag auch wenn die ListView nicht den Fokus hat (sondern z.B. gerade der Hinzufügen-Button oder Filter definierende Schaltflächen.

    Ansonsten gehe ich davon aus, dass DBItem as DBItemTyp (eigener Typ/Klasse) deklariert wurde und Abfrageergebnis eine List(Of DBItemTyp) ist, die aus einer SQL-Abfrage gefüllt wurde.

    Hoffe das hilft erstmal weiter, nähere Informationen sollte die OH zum Thema ListView erbringen.

    Gruß
    Martin
    Zuletzt editiert von M.Dietz; 14.08.2008, 11:32. Reason: Variablenumbenennung wegen reservioerter namen

    Comment


    • #3
      Vielen Dank! Klappt wunderbar!

      Doch weil das Ganze in einer ListView angezeigt wird, weiss ich nicht mehr, wie ich den aktuell markierten Datensatz auswählen kann (z.B. zum ändern).

      Ich hab das ganze schon bei einer Listbox gelöst:

      Code:
      Dim UniID As New ArrayList
      ...
      
      cmd.CommandText = "SELECT * FROM tabelle"
      ...
      
      UniID.Add(reader("Unique"))
      ...
      
      cmd.CommandText = "SELECT * FROM tabelle WHERE Unique=" & UniID(ListBox1.SelectedIndex)
      Wenn ich das so bei der ListView lösen möchte, kommt der Fehler, dass 'der Wert nicht in Integer konvertiert werden kann'.

      Wie bekomme ich bei der ListView eine Identifizierung meines Datensatzes hin?

      Danke und Gruß, Syn

      Comment


      • #4
        Was meinst Du mit "den aktuell selektierten Datensatz auswählen"?

        Abfragen welche Zeile/n selektiert ist/sind geht über die Collection ListView1.SelectedItems. Angenommen, du hast ListView1.MultiSelect auf False, dann ist da auch nur maximal einer drin, in welcher Reihenfolge die drin sind, wenn du Mehrfachselektion zugelassen hast, kann ich leider nicht sagen, ich vermute aber in der Reihenfolge der Selektierung.

        Du musst zuerst abfragen, ob überhaupt einer selektiert ist über Listview1.SelectedItems.Count. Dann kriegst Du den ersten über ListView1.SelectedItems(0), und die UniqueDBId des angezeigten Datensatzes erhälst du dann über Listview1.SelectedItems(0).Tag, vorausgesetzt, Du hast die Liste gefüllt wie ursprünglich beschrieben.

        Also:
        Code:
        Dim sqlStr as String
        If Listview1.SelectedItems.Count=0 then
           MsgBox("Fehler",vbCritical)
           Return
        End If
        if ListView1.SelectedItems.Count=1 then
           sqlStr = "SELECT * FROM Tabelle WHERE Unique=" & ListView1.SelectedItems(0).Tag
        Else ' Count > 1
           Dim IDList as String=""
           For i As Integer=0 to ListView1.SelectedItems.Count-1
              IDList = IDList & "," & ListView1.SelectedItems(i).Tag
           Next
           IDList=IDList.TrimStart(",")
           sqlStr="SELECT * FROM Tabelle WHERE UniqueID IN (" & IDList & ")"
        End If
        Grundsätzlich solltest Du NIEMALS eine DatenbankID mit der Zeilennummer des Anzeigeelements koppeln, sondern immer mit dem Anzeigeelement selbst. Die Zeilennummer ist als Eigenschaft von der Anzeige abhängig, und die kann sich ändern, je nachdem ob Du die Anzeige filterst, anders sortierst oder andere Elemente, wie z.B. Trenner in die Anzeige einfügst. Damit spiegelt die Zeilennummer in der ListBox nicht mehr unbedingt den Index in Deiner UniID-Collection wider. Der "Trick" mit dem Tag des ListViewItems sorgt dafür, dass das ausgewählte Element selbst die Information enthält, die Du brauchst, und da dies ein Object ist, könntest Du theoretisch hinter das Tag sogar das komplette DBItem hängen, um auf die benötigten Informationen zuzugreifen.

        Willst Du einen entsprechenden Eintrag per Code selektieren, bleibt Dir nichts anderes übrig, als erstmal alle Einträge durchzugehen, bis du den gesuchten gefunden hast, und dann die Selected Eigenschaft dieses Items zu setzen. Deinem Beispielcode zufolge denke ich aber Du meintest ersteres.

        Comment


        • #5
          Jup... genau so muss das sein.
          Vielen Dank!!
          Syn

          Comment


          • #6
            Also, ich muss mich doch berichtigen. Genau so SOLLTE es sein.
            Bei der ersten Auswahl klappt das Ganze. Wähle ich allerdings den nächsten Datensatz aus, läuft es auf einen Fehler weil der ListView1.SelectedItems.Count 0 ist.

            Desweiteren verschliesst sich mir im Moment noch wie der ListView1.SelectedItems(i).Tag den Datensatz in der SQL-Tabelle entziffern soll.
            Mein Uniqueeintrag auf SQL ist einfach eine fortlaufende Nummer, welche beim Erstellen das Datensatzes automatisch weiter geführt wird.

            Starte ich nun die Abfrage aus VB heraus meine ListView zu befüllen, gebe ich dort schon eine Sortierbedingung für SQL mit. Will heissen, dass die Reihenfolge im Listview nicht mehr unbedingt der im SQL entspricht.
            Aber davon geht doch Dein Code-Schnipsel aus, oder?

            Kannst Du mir da etwas Erleuchtung bringen?

            Danke schon im Voraus!
            Syn

            Comment


            • #7
              Nabend Syn.
              Ich muss gestehen, dass ich schon ziemlich müde bin und nurnoch über alle neuen Artikel eben drüber fliege. Hoffe dir aber trotzdem helfen zu können.

              So also zu deiner benötigten ID. Warum liest du nicht einfach auch die ID mit in dein DataGridView und versteckst die ganze Spalte dann? Dann würdest du sehr einfach an deine benötigte ID rankommen, auch mit anderer Sortierung.

              Zum anderen anderen könntest du auch an den Datensatz rankommen indem du deine WHERE Klausel erweiterst. WHERE DBName = ViewName AND DBNachname = ViewNachname AND ...

              Problem dabei, dass das ganze dann nicht absolut eindeutig ist!

              Starte ich nun die Abfrage aus VB heraus meine ListView zu befüllen, gebe ich dort schon eine Sortierbedingung für SQL mit. Will heissen, dass die Reihenfolge im Listview nicht mehr unbedingt der im SQL entspricht.
              Hat er gesagt..

              Grundsätzlich solltest Du NIEMALS eine DatenbankID mit der Zeilennummer des Anzeigeelements koppeln, sondern immer mit dem Anzeigeelement selbst. Die Zeilennummer ist als Eigenschaft von der Anzeige abhängig, und die kann sich ändern, je nachdem ob Du die Anzeige filterst, anders sortierst oder andere Elemente, wie z.B. Trenner in die Anzeige einfügst. Damit spiegelt die Zeilennummer in der ListBox nicht mehr unbedingt den Index in Deiner UniID-Collection wider.

              EDIT: Nochmal Kommando zurück.. Eben gemerkt, dass M.Dietz das ganze über die Tag Eigenschaft gelößt hat, was wesentlich eleganter ist, als eine zusätzliche Spalte. Die Tag Eigenschaft der 1. Spalte enthält deine benötigte Datenbank ID für die ganze jeweilige Zeile (egal wie sortiert). Vorrausgesetzt du hast dein ListView gefüllt, wie M.Dietz es beschrieben hatte.
              Zuletzt editiert von Myst; 26.08.2008, 22:43. Reason: Ergänzung

              Comment


              • #8
                Auch hier gilt wieder: Ein Datensatz ist durch einen eindeutige Identifier zu kennzeichnen, der nichts mit einer Ausgabereihenfolge zu tun hat. Er sollte unabhängig von den Informationen in diesem Datensatz sein, es sei denn Du kannst sicherstellen, dass er eindeutig ist und sich die gesamte Lebenszeit dieses Eintrags nicht ändert (sonst kriegst Du Schwierigkeiten beim Verweis aus anderen Tabellen). Dieses eindeutige Feld nennt man "Schlüsselfeld", oder englisch "Key" und üblicherweise wird man von den Datenbanksystemen gewarnt, wenn man versucht eine Tabelle ohne Schlüsselfeld zu erzeugen, oder sie bieten sogar an, selbst einen AutoWert als Schlüssel anzulegen. Wenn ich Dich richtig verstanden habe, ist das genau das, was du als fortlaufende Nummer beim Erstellen des Datensatz referenzierst. Dieser Identifier (oder die Identifiers wenn Du eine Abfrage aus Joins hast) sollte(n) bei jeder Abfrage mit in die SELECT Klausel hinein, es sei denn Du bist absolut sicher, dass Du hinterher nicht wieder auf einen spezifischen Datensatz zugreifen musst. Damit kannst Du den ausgelesenen Datensatz immer identifizieren, egal welche Sortierbedingung in deinem SQL-Statement steht.

                Wenn Du dann in der DB-Schnittstelle Deine Datenbankanfrage auswertest, um die DBItems zu füllen, sollte dieser Identifier in das Feld DBItem.ID, wenn Du dann Deine Listview mit den DBItems füllst hast Du automatisch im Tag exakt die ID, die in der Datenbank als Schlüssel für den Eintrag benötigt wird.

                Wie ich auch schon geschrieben habe, ist es extrem gefährlich einen Index in einer Liste zu nutzen. Die ID braucht nur eine Eigenschaft: sie muss eindeutig sein, sie in irgend einer anderen Art und Weise auswerten zu wollen, wie z.B. dass eine ID größer ist als eine andere, kann nur zu Fehlern führen. Selbst wenn man glaubt, dass höhere IDs später angelegt wurden, kann man auf die Nase fallen, wenn im DBMS ein Import definiert ist, der einen Datensatz inklusive ID importieren kann, dann kann nämlich ein Datensatz mit der ID 15 importiert werden, obwohl der Zähler eigentlich längst bei 37 ist, nur weil der Datensatz 15 in der Vergangenheit gelöscht wurde, und diese ID daher wiede "frei" ist.

                Ich hoffe es wird jetzt einigermaßen klar, warum ich hier mit dem Tag arbeite, und nicht mit den angezeigten Datenbankinhalten; ihm kommt sozusagen eine Schlüsselrolle in der Datenanzeige zu


                Wieso beim Auswerten des nächsten Datensatzes SelectedItems.Count 0 sein soll, kann ich jetzt nicht nachvollziehen, das muss an Deinem Code liegen, da musst Du nachsehen, was Du machst, um den ersten gewählten Eintrag zu bearbeiten. Der häufigste Fehler ist hierbei, dass nach jedem Datensatz die Übersicht neu aufgebaut wird, was üblicherweise dadurch geschieht, dass man das Listview löscht und neu füllt. Wenn das unbedingt notwendig ist, musst Du Dir vorher eine Liste der selektierten IDs halten, und die beim Neufüllen der ListView wieder selektieren, ansonsten rate ich grundsätzlich dazu, zuerst die ganze Selektion abzuarbeiten, bevor die Anzeige aktualisiert wird.

                Gruß
                Martin

                Comment


                • #9
                  Vielen Dank für Eure vielen Tips.
                  Leider steh ich immer noch voll auf der Leitung.

                  @Martin:
                  Das mit der Eindeutigkeit, dem DB-Key, den Gefahren usw. ist alles einleuchtend.
                  Deshalb habe ich in meiner DB auch ein Feld der fortlaufenden Nummer, das von keinem bearbeitet werden kann und erst stribt, wenn der Datensatz gelöscht wird. Eine Sortierung ooder Auswertung aufgrund dier Nummer ist nicht vorgesehen - sie dient ausschliesslich zur Identifikation.

                  Da ich mir jetzt nicht sicher bin, ob ich Deinen Code-Schnipsel richtig interpretiert hier meine Version:
                  Code:
                  reader = cmd.ExecuteReader
                          ListView1.Items.Clear()
                  
                          For Each DBItem In reader
                              Dim itm As ListViewItem = ListView1.Items.Add(reader("Nachname"))
                              itm.Tag = reader("AdrNR")
                              itm.SubItems.Add(reader("Vorname"))
                              itm.SubItems.Add(reader("Strasse"))
                              itm.SubItems.Add(reader("Ort"))
                              itm.SubItems.Add(reader("Infos"))
                          Next
                   
                          reader.Close()
                  Wenn ich Dich nun richtig verstanden hab, sollte die itm.Tag für jede Zeile die eindeutige Nummer des Datensatzes beinhalten.
                  ...und hier wieder auftauchen:
                  Code:
                  ...
                  if ListView1.SelectedItems.Count=1 then
                     sqlStr = "SELECT * FROM Tabelle WHERE AdrNR=" & ListView1.SelectedItems(0).Tag
                  Else ' Count > 1
                  ...
                  End If
                  Ist meine Denke richtig??
                  Wenn ja, dann kommt dieser Tag leider nicht an. Der SelectedItems(0).Tag bleibt auf 'Nothing'.

                  Die Übersicht wird nicht neu aufgebaut. Das passiert nur, beim Hinzufügen, Editieren oder Löschen eines Datensatzes. Dann würde wieder 'Code 1' ausgeführt.
                  Beim anklicken und auswählen passiert nichts dergleichen.

                  Danke und Gruß
                  Syn

                  Comment


                  • #10
                    Ich habe auch öfter Probleme mit dem Tag weshalb ich zu einer, wenn auch sehr unschönen, aber funktionierenden, Lösung gekommen bin.

                    Ich schreibe den Schlüssel nicht in den .Tag sondern in die erste Spalte und setze diese auf Größe 0 und sorge dafür das die nicht vergrößert werden kann. Wenn jetzt jemand was auswählt nehme ich halt den Wert aus der erste Spalte statt aus dem Tag.

                    Gruß Womble

                    Comment


                    • #11
                      ...das war Gedankenübertragung!
                      Genau das hab ich nun gemacht. Meine fortlaufende Datensatznummer dem ganzen vorangestellt und mit der gearbeitet.
                      Bei der ersten Auswahl eines Datensatzes klappt das auch wunderbar.
                      Bei der zweiten Auswahl wird Listview1. SelectedItems.Count sofort beim Klick auf Null gesetzt. Dies wiederum zieht dann die Fehlermeldung 'InvalidArgument=Value mit dem Wert 0 ist für index ungültig. Parametername: index' nach sich.
                      Das (für mich) verwunderliche: Nach der Ausgabe des Fehlers läuft der Prozedurschritt nochmal ab und der Code wird ohne Fehler abgespult.

                      Was kann ich tun, damit ich den Fehler behoben bekomme? Und warum wird das SelectedItems.Count auf Null gesetzt, wenn ich doch etwas selektiere?!

                      Gruß
                      Syn

                      Comment


                      • #12
                        Wenn dein Tag Nothing ist, dann heisst das, dass reader("AdrNr") Nothing zurückgegeben hat. Nur weil da aber kein String generiert wird, der an den SQL-String angehängt wird, heisst das noch lange nicht, dass im Tag Nothing drinsteht, vielleicht ist da nur ein Objekt drin, dessen ToString() Funktion nichts zurückgibt. Grundsätzlich solltest Du in dem Tag aber auch nicht das Objekt speichern, das Dir reader("AdrNr") zurückliefert, sondern die ID aus der Datenbank, und die ist Integer, also itm.Tag=CType(reader("AdrNr"),Integer) oder, falls das als String zurückkommt mit itm.Tag=Integer.TryParse(reader("AdrNr")). Wenn Du das so abspeicherst, ist beim Auslesen auch was drin.

                        Du solltest mal überprüfen, wie reader(...) aufgebaut ist, ich glaube kaum, dass dies eine Collection (of DBItem) ist, daher kann for each DBItem in reader nicht funktionieren, und dass ein Zugriffparameter (auf die Spalte) reichen soll, um eine Datenbankanfrage (deren Ergebnis aus Reihen und Spalten besteht) auszulesen wage ich auch stark zu bezweifeln.

                        Angenommen Du hast eine Klasse clsDatabaseInterface, das für Dich die Query ausführt und als List(Of clsDataset) im Objekt abspeichert, und die Klasse clsDataset, die den einzelnen Datensatz (eine Zeile) des Abfrageergebnisses darstellt, den Zugriff auf die Spalten mittels getString, getInt, getFloat usw. je nach Spaltendefinition ermöglicht, dann ergibt sich folgende Vorgehensweise:

                        Code:
                        dim db as new clsDatabaseInterface
                        db.OpenDB(<Datenbank>)
                        if not db.sqlQuery("SELECT * FROM Adressen WHERE ...") then return
                        for each ds as clsDataset in db.QueryResult
                           Dim itm As ListViewItem = ListView1.Items.Add(ds.getString("Nachname"))
                           itm.Tag = ds.getInt("AdrNR")
                           itm.SubItems.Add(ds.getString("Vorname"))
                           itm.SubItems.Add(ds.getString("Strasse"))
                           itm.SubItems.Add(ds.getString("Ort"))
                           itm.SubItems.Add(ds.getString("Infos"))
                        Next
                        Ansonsten: Wann wird denn Dein Code aufgerufen, der die ListView.SelectedItems auslesen soll? Nach dem was Du schreibst, scheint das im Code des Handlers für das Klick-Ereignis zu passieren, der früheste Zeitpunkt, an dem ListView.SelectedItems ausgewertet werden darf, ist aber ListView_SelectedItemsChanged. Und wenn nicht die Information für andere Anzeigelemente davon abhängen, würde ich selbst dann davon abraten, sondern erst auf expliziten Button-Click oder ähnliches eine DB-Auswertung auslösen.

                        Comment

                        Working...
                        X