Databinding mit DataGridView

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 16 Antworten in diesem Thema. Der letzte Beitrag () ist von Peter329.

    Databinding mit DataGridView

    Hi,

    ich beschäftige mich mit dem "Databinding". Ich möchte alle verfügbaren Laufwerke in einer DataGridView anzeigen:

    VB.NET-Quellcode

    1. Dim _Drives As DriveInfo()
    2. _Drives = DriveInfo.GetDrives
    3. dgvDrives.DataSource = _Drives


    Das klappt auch ... ich erhalte eine DatagridView mit 10 Spalten: Name, DriveType, DriveFormat, IsReady ...

    Dann springen jedoch Fehlerfenster auf, weil bestimmte Laufwerke nicht ready sind und deshalb die get_DriveFormat Methode kracht. Das habe ich wie folgt abgefangen:

    VB.NET-Quellcode

    1. Private Sub dgvDrives_DataError(sender As Object, e As System.Windows.Forms.DataGridViewDataErrorEventArgs) Handles dgvDrives.DataError
    2. i += 1
    3. Debug.Print(Format(DateTime.Now, "dd.MM.yyyy hh:mm:ss") & " " & Format(i, "n0").PadLeft(5, " "c) & ": " _
    4. & sender.ToString & " - " & e.ToString)
    5. End Sub


    Braucht man so eine dumme Ereignisprozedur? Oder kann man den get_DriveFormat von vornherein abklemmen, wenn IsReady.Checked falsch ist?

    Auch ansonsten gefällt mir an der Sache das eine oder andere noch nicht:

    1. Ich würde gern auswählen, WELCHE SPALTEN zu sehen sind. Die Eigenschaft "dgvDrives.DisplayMember" suche ich vergeblich. Ist ja auch klar, ich muss jetzt ja eine LISTE von Spalten benennen. Ich hab das mit "dgvDrives.DataMember" versucht, komme damit aber nicht klar!

    2. Ich würde gern nur die Drives anzeigen, die auch verfügbar sind. Also etwa über die Abfrage

    If not Directory.Exists(dgvDrives.Rootdirectory) Then ... 'drive nicht anzeigen

    Aber wie kann ich denn aus _Drives Zeilen entfernen? _Drives.Remove(...) suche ich vergeblich.

    3. Die Spalte "AvailableFreeSpace" ist numerisch - die würde ich gern formatieren also statt "251186618368" vielleicht besser "251.2 GB"

    Meine "hausbackene" Lösung wäre, die Liste "_Drives" zu lesen und daraus eine geeignete neue Liste "_DrivesDisplay" zu basteln, die ich dann per Databinding mit der DataGridView verbinde.

    Ist das die "richtige" Vorgehensweise? Oder geht das eleganter?

    LG
    Peter

    Peter329 schrieb:

    weil bestimmte Laufwerke nicht ready sind
    Mach das über eine List(Of DriveInfo), hier mal am Beispiel einer ListBox:

    VB.NET-Quellcode

    1. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    2. Dim dr = New List(Of IO.DriveInfo)
    3. dr.AddRange(IO.DriveInfo.GetDrives)
    4. For i = dr.Count - 1 To 0 Step -1
    5. If Not dr.Item(i).IsReady Then
    6. dr.RemoveAt(i)
    7. End If
    8. Next
    9. Me.ListBox1.DataSource = dr
    10. Me.ListBox1.DisplayMember = "Name"
    11. End Sub
    12. Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
    13. Me.ListBox1.DisplayMember = "AvailableFreeSpace"
    14. End Sub
    15. Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
    16. Me.ListBox1.DisplayMember = "VolumeLabel"
    17. End Sub
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Dein Rat ist einfach KLASSE!

    Damit ist zum einen das Problem gelöst, dass unnötige Exceptions (da vorhersehbar) ausgelöst werden. Zum anderen kann man damit ungewünschte Einträge aus der Liste entfernen.

    Also mein Daumen ist definitv und begeistert oben!

    So ... jetzt bleibt noch die Sache mit der DataGridView ...

    In der ListView kann man bestimmte Spalten auswählen. Das hast du in deinem Posting gezeigt ... aber das hab ich auch schon gewusst ... wenn ich das bei aller Bescheidenheit anmerken darf.

    Aber wie geht das jetzt mit der DataGridView. Wie kann ich da nur bestimmte Spalten anzeigen ... und möglichst die Darstellung in der DGV noch formatieren? Irgendwie verstehe ich die Befüllung einer DGV mittels Databinding nicht so richtig.

    LG
    Peter

    Peter329 schrieb:

    DGV mittels Databinding
    Sieh Dir mal diesen Post an.
    Füge der Form einen weiteren Button hinzu:

    VB.NET-Quellcode

    1. Private Sub Button4_Click(sender As Object, e As EventArgs) Handles Button4.Click
    2. Me.DataGridView1.Columns(1).Visible = Not Me.DataGridView1.Columns(1).Visible
    3. End Sub

    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Also mit dgvDrives.Columns(n).Visible = False kann ich bestimmte Spalten von der Anzeige ausblenden. Da hat mir dein Rat schon mal ein gutes Stück weitergeholfen. Damit geht mein Daumen hoch! :D

    Aber wie kann ich den Inhalt der DGV Spalten formatieren? Oder die Reihenfolge der Anzeige verändern?

    Bei dem eingestellten Link beziehst du dich vermutlich auf Post #22 ... aber irgendwie komme ich damit auch nicht so recht klar ...

    Peter329 schrieb:

    nicht so recht klar
    Dann nehmen wir die Nur-DGV-Variante:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Public Class Form1
    2. Private table As DataTable
    3. Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    4. InitDataTable()
    5. InitData()
    6. End Sub
    7. Private Sub InitDataTable()
    8. Me.table = New DataTable()
    9. ' für das DGV
    10. Me.table.Columns.Add(New DataColumn() With { _
    11. .ColumnName = "Coordinate", _
    12. .DataType = GetType(Double) _
    13. })
    14. ' für das DGV
    15. Me.table.Columns.Add(New DataColumn() With { _
    16. .ColumnName = "Value", _
    17. .DataType = GetType(Double) _
    18. })
    19. End Sub
    20. Private Sub InitData()
    21. ' DataSource löschen
    22. Me.table.Clear()
    23. Me.table.TableName = "Sinus"
    24. Me.DataGridView1.DataSource = Nothing
    25. End Sub
    26. Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    27. ' Hier werden die Daten bereitgestellt:
    28. For i As Integer = 0 To 100
    29. Dim newRow As DataRow = Me.table.NewRow()
    30. newRow(0) = i
    31. newRow(1) = Math.Sin(i / 10)
    32. Me.table.Rows.Add(newRow)
    33. Next
    34. Me.DataGridView1.DataSource = Me.table
    35. End Sub
    36. Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
    37. Using dlg As New SaveFileDialog
    38. dlg.Filter = "XML-Dateien|*.xml"
    39. If dlg.ShowDialog <> Windows.Forms.DialogResult.OK Then
    40. Return
    41. End If
    42. Me.table.WriteXml(dlg.FileName)
    43. End Using
    44. End Sub
    45. Private Sub Button3_Click(sender As System.Object, e As System.EventArgs) Handles Button3.Click
    46. Using dlg As New OpenFileDialog
    47. dlg.Filter = "XML-Dateien|*.xml"
    48. If dlg.ShowDialog <> Windows.Forms.DialogResult.OK Then
    49. Return
    50. End If
    51. Me.table.ReadXml(dlg.FileName)
    52. End Using
    53. Me.DataGridView1.DataSource = Me.table
    54. End Sub
    55. Private Sub Button4_Click(sender As Object, e As EventArgs) Handles Button4.Click
    56. Me.DataGridView1.Columns(1).Visible = Not Me.DataGridView1.Columns(1).Visible
    57. End Sub
    58. End Class
    Zum Formatieren gugst Du hier.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

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

    Man kann ein DGV auch ohne typisiertes Dataset designergestützt designen, mit allem Pipapo. Statt eines typDatasets erstellt man halt eine "ObjectDataBase".
    Hab ich hier ein Video zu:
    Keine Strings in die File-Listbox!
    Die DataSource ist zwar an eine Listbox gebunden, aber einfachrer noch kannst dus an ein Dgv binden, nämlich wie gewohnt die Entität aus dem Datenfenster aufs Form schmeißen.

    Ich hoffe, hilft weiter, weil das mit der crashenden get_DriveFormat - Funktion habich nicht verstanden - die kenne ich nicht, ich weiß nur, eine Funktion kann via Databinding garnet addressiert werden - also wie soll da was crashen.

    Dieses blöde DataError-Event ist der allerletzte Notnagel.
    Da muss man schon ganz genau wissen, wie es kommt, dass das DGV scheinbar an ungültige Daten gebunden wird, und warum es doch unbedingt so sein muss.
    Sieh lieber zu, konsinstente Daten in der Datenquelle zu haben.
    Erst mal Danke für die Ratschläge.

    Ich habe den Ansatz von RFG mit der typisierten Tabelle weitgehend realisieren können:

    VB.NET-Quellcode

    1. ' Define table
    2. Me.table = New DataTable()
    3. Me.table.Columns.Add(New DataColumn() With {
    4. .ColumnName = "Name",
    5. .DataType = GetType(String)
    6. })
    7. Me.table.Columns.Add(New DataColumn() With {
    8. .ColumnName = "Volume Label",
    9. .DataType = GetType(String)
    10. })
    11. Me.table.Columns.Add(New DataColumn() With {
    12. .ColumnName = "Size",
    13. .DataType = GetType(String)
    14. })
    15. Me.table.Columns.Add(New DataColumn() With {
    16. .ColumnName = "Total Size",
    17. .DataType = GetType(String)
    18. })
    19. Me.table.Columns.Add(New DataColumn() With {
    20. .ColumnName = "Percent",
    21. .DataType = GetType(String)
    22. })
    23. ' Get drive list
    24. Dim dr = New List(Of IO.DriveInfo)
    25. dr.AddRange(IO.DriveInfo.GetDrives)
    26. ' Fill table
    27. For Each drEntry In dr
    28. If drEntry.IsReady Then
    29. Dim newRow As DataRow = Me.table.NewRow()
    30. With drEntry
    31. newRow(0) = .Name
    32. newRow(1) = .VolumeLabel
    33. newRow(2) = Format((.TotalSize - .AvailableFreeSpace) / 1024 / 1024 / 1024, "n1") & " GB"
    34. newRow(3) = Format(.TotalSize / 1024 / 1024 / 1024, "n1") & " GB"
    35. newRow(4) = Format((.TotalSize - .AvailableFreeSpace) / .TotalSize * 100, "n1") & " %"
    36. End With
    37. Me.table.Rows.Add(newRow)
    38. End If
    39. Next
    40. ' Data binding
    41. Me.dgvDrives.DataSource = Me.table


    Zwei Dinge würde ich noch gern ändern:

    1. Die Spalten werden gemäß der DefaulCellStyle Eigenschaften linksbündig ausgegeben. Das ist für die Spalten "Name" und "Volume Label" so auch richtig. Bei den Spalten "Size" etc. soll die Ausgabe aber RECHTSBÜNDIG erfolgen. Ich habe jetzt lange nach Eigenschaften wie .CellStyle oder .Align etc. gesucht aber nichts Passendes gefunden. Auch der verlinkte MSDN Beitrag hilft mir da leider nicht weiter.

    2. Standardmäßig wird die erste Zeile ausgewählt. Ich möchte aber zu Beginn KEINE Zeile auswählen. Bei ListBoxes setze ich da den SelectedIndex auf -1. Bei der DataGridView gibt es den aber nicht, sondern wohl nur eine Collection. Wie bediene ich das denn?

    Peter329 schrieb:

    Tabelle
    Size und Percent sollten nicht vom Typ String sein.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    dassis eine untypisierte DataTable.

    Sollte man ja nach Möglichkeit vermeiden, und stattdessen ein typisiertes Dataset aufsetzen.

    Wobei in speziell diesem Falle ja eine ObjectDatasource viel smarter wäre.

    Mit beidem (typDataset, ObjectDatenquelle) kann man im FormDesigner die Darstellung differenziert und wysiwyg gestalten.
    @Peter329 Double und entsprechend formatieren:

    VB.NET-Quellcode

    1. Me.DataGridView1.Columns(1).DefaultCellStyle.Format = "0.00 GB"

    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Also das klappt bei mir leider nicht.

    VB.NET-Quellcode

    1. Me.table.Columns.Add(New DataColumn() With {
    2. .ColumnName = "Size",
    3. .DataType = GetType(Double)
    4. })
    5. newRow(2) = (.TotalSize - .AvailableFreeSpace) / 1024 / 1024 / 1024
    6. Me.dgvDrives.Columns(2).DefaultCellStyle.Format = "0.0 GB"


    Die Ausgabe der Spalte erfolgt nach wie vor LINKSbündig.

    Selbst wenn ich die .DefaultCellStyle.Format Zuweisung auskommentiere wird die Zahl immer noch linksbündig ausgegeben.

    Any bright ideas? :)

    [edit]

    Ouch ... ich darf mal ausnahmensweise meine Frage selbst beantworten:

    VB.NET-Quellcode

    1. Me.dgvDrives.Columns(2).DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight


    Damit flutscht die Formatierung! :)

    Somit verbleibt noch meine zweite Frage:

    2. Standardmäßig wird die erste Zeile ausgewählt. Ich möchte aber zu Beginn KEINE Zeile auswählen. Bei ListBoxes setze ich da den SelectedIndex auf -1. Bei der DataGridView gibt es den aber nicht, sondern wohl nur eine Collection. Wie bediene ich das denn?

    Mehr durch Zufall hab ich folgende Technik gefunden:

    VB.NET-Quellcode

    1. Me.dgvDrives.Rows(dgvDrives.CurrentCellAddress.Y).Selected = False


    Das funktioniert zwar ... aber ich habe keine so rechte Ahnung warum! Ob das eine "saubere" Lösung ist? Wie bei allen Lösungen, die ich nicht verstehe, hab ich damit ein ungutes Gefühl!

    LG
    Peter

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

    ErfinderDesRades schrieb:

    WinForms-Databinding tickt so, dass immer ein Item angewählt ist, es sei denn, es gibt keine Items.
    Will man Databinding, aber unbedingt auch nichts auswählen können, so muss man u.U. einen Extra-Datensatz schaffen, der "nichts angewählt" bedeutet.


    Neeee, also da bevorzuge ich dann doch lieber die Sache mit der CurrentCellAddress, obwohl mir das im Sinne der reinen Lehre nicht so ganz koscher erscheint, weil das ja wohl eher eine graphische Methode ist. Na, der Zweck heiligt die Mittel ... und skrupellos wie ich nun mal bin, mache das ganz einfach so. :)

    ErfinderDesRades schrieb:

    (Meist finde ich, es gibt wichtigeres.)


    Nu, das hängt doch ganz eindeutig von den Design Anforderungen der Anwendung ab. Und über die wollen wir doch nicht diskutieren! Oder etwa doch? hehehe

    Jetzt aber zur Sache: Die Technik mit der Tabelle scheint mir "ausgekaut". Mein Fazit: das funktioniert ... aber man muss eben schon sehr viele Dinge im Programm abdecken, die ich eigentlich lieber im Designer sehen würde.

    Deshalb werde ich mir jetzt dein Video zu Gemüte führen ... und den Ansatz mit ObjectDataSource aufnehmen. Mal sehen, wie das ausschaut ...