List(of String) an DatagridView binden

  • VB.NET

Es gibt 37 Antworten in diesem Thema. Der letzte Beitrag () ist von Takafusa.

    Das ist nicht korrekt.
    Sobald man eine Public Property hat, kann man kompilieren und danach die BS an diesen Typ binden. Entweder per Designer, indem man der BS als DataSource einen neue Datenquelle heraussucht und dann den Typ aus dem Projekt hernimmt, oder per Code in der FormX.Designer.VB, wo man bei der BS angibt: DeineBindingSource.DataSource = GetType(DieKlasse)
    Bilder
    • BindBindingSourceToType.png

      45,73 kB, 1.514×522, 85 mal angesehen
    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 schrieb:

    Du haust Dir ne BS auf's Form, bindest sie an den Typ Transponder und das DGV an die BS. Dann kannst Du im Designer einstellen, wie das DGV aussehen soll, ohne dass Du während der Laufzeit irgendwas designtechnisch einstellen musst.


    Dann hab ich das also vollkommen falsch verstanden.

    Mit anderen Worten: Ich arbeite mit einer leeren DGV ohne irgendwelche Spalten, denn die Spalten werden durch den Bind generiert ... und dann muss ich die Properties der Spalten halt im Programm einstellen. Damit kann ich leben.

    Wie mache ich das denn jetzt mit dem Filter ? Ich will beispielsweise alle Zeilen ausblenden, wo das Lvl > 3 ist ...

    Die BindingSource1 hat die Methode .Filter mit zig Parametern. Nicht allle sagen mir etwas :) ... wo gebe ich die Bedingung Lvl <= 3 ein ... und außerdem, an welcher Stelle im Programfluss muss ich das dann aufrufen ?

    LG
    Peter

    edit: die letzten beiden Beiträge habe ich wegen des Seitenumbruchs erst jetzt gesehen .... na, da bin ich gespannt, ob ich das mit dem Designer doch noch "gebacken" bekomme ... ?

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

    Oder indem Du eben anderweitig die BS/-DGV-Daten filterst: LINQ.

    VB.NET-Quellcode

    1. DeineBindingSource.DataSource = TransponderList.Where(Function(x) x.Level < 3)

    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.
    und in einem typDataset ein geeignetes DataModel anlegen ist keine Option?
    Das wäre ja in 10min erledigt.
    An sowas zu binden, mit BindingSources, ist glaub bekannt.
    Dann könnte man auch filtern und sortieren - alles Kinderspiel.

    Die eigentliche Arbeit wäre, die TLL-Datei ins Dataset einzulesen.

    Aber diese oder eine vergleichbare Arbeit muss glaub eh geleistet werden.
    Das mit dem Dataset werde ich ausprobieren. Aber ich möchte halt nicht ständig mein Vorhaben ändern ... und deshalb würde ich jetzt gern erst mal die Sache mit dem Databinding klären.

    Irgendwie ist mein Wissen um die Zusammenhänge sehr schwammig. Jedenfalls tue ich mich außerodentlich schwer im Fehlerfall da irgendwie zielgerichtet vorzugehen.

    Wo stehe ich:

    Der Aufbau der DGV klappt hervorragend. Mit und ohne BindingSource. (s. Anhang)

    Das aus- und einblenden kriege ich ohne BindingSource hin. Da baue ich einfach die Datasource from Scratch neu auf und lade eben bestimmte Zeilen nicht in die list(of Transponder). Methode Holzhammer ... das will ich gern zugestehen.

    Wenn ich das jetzt eleganter mit BindingSource und Filter versuche, dann erhalte ich eine leere DGV mit genau einer Spalte. (s. Anhang)

    Das ist mein coding:

    VB.NET-Quellcode

    1. Private Sub cmdReload_Click(sender As Object, e As EventArgs) Handles cmdReload.Click
    2. BindingSource1.DataSource = TransponderList.Where(Function(x) x.Lvl < viewLvl)
    3. 'ReloadTransponder()
    4. End Sub


    Wie schon gesagt, ich tue mich sehr schwer mit einer vernünftigen Fehlerdiagnose, weil ich einfach nicht weiß, wie diese Befehle funktionieren und tracen kann man das Zeug ja schon gar nicht.

    Ich bin (wieder einmal) verwzweifelt ! Kann mich jemand aus diesem Tal abholen ? Bitte ! Please ! :)

    LG
    Peter
    Bilder
    • s 2020-08-20 17-32-404.jpg

      76,99 kB, 923×593, 76 mal angesehen
    • s 2020-08-20 17-33-014.jpg

      10,36 kB, 923×593, 64 mal angesehen

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

    Peter329 schrieb:

    Aber ich möchte halt nicht ständig mein Vorhaben ändern ...
    Du musst Deine Herangehensweise ändern.
    Bei einem komplexen Programm ist es absolut sinnvoll, wenn nicht gar notwendig, wenn Du einzelne Probleme in einem separaten Projekt bearbeitest und löst.
    Ich rate da immer, das Projekt in disjunkte Arbeitspakete zu zerlegen und diese alle in je einem separaten Projekt zu bearbeiten.
    Wenn dann (fast) alle Teilprojekte erstellt sind, werden die dann zu einem neuen Gesamtprojekt zusammengefügt.
    Die Teilprojekte packst Du dann in Deine Testprojekt-Sammlung, und irgendwann kannst Du ein neues Projekt aus vorhandenen Teilprojekten zusammensetzen.
    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!

    Takafusa schrieb:

    ch hab dir ein kleines Video gemacht, das ist alles schnell und einfach gemacht.


    Das war natürllich sehr nett von dir, vielen Dank ! Man sieht wie du einen Dataset definierst und den dann in der FormLoad Prozedur an eine DGV bindest. Offensichtlich klappt dann die Sache mit "Filter". Dieses Vorgehen hat ja auch schon EDR angeraten.

    RodFromGermany schrieb:

    Du musst Deine Herangehensweise ändern.


    Also das möchte ich jetzt zu meiner Ehrenrettung nicht so stehen lassen. Ich weiß nämlich schon, wie wichtig logischer Design und Modularisierung sind.

    Was den logischen Design betrifft, so handelt es sich um eine Tabelle mit drei Spalten ... Die Spalte "Seq" ist der primary key und die Spalten "Lvl" und "Tag" sind direkt davon abhängig. Damit sind die erste und zweite Normalform erfüllt. Die dritte Normalform wird verletzt, weil die Indentation von "Tag" aus "Lvl" abgeleitet werden kann - aber das ist im Sinne der Denormalisierung legal. Um der reinen Lehre doch noch zum Recht zu verhelfen, könnte ich das Anbringen der Indentation z.B. ins CellFormatting Event verlegen, wenn das nicht zu schlecht für die Performance sein sollte. Mehr gibt es zum logischen Design der Tabelle m.E. nicht zu sagen.

    Was die Modularisierung anbelangt, so enthält mein Modul die Funktionen:

    - Lesen der .tll Datei
    - Anzeigen der .tll Datei mit key, level und (indented) Tag
    - Ein- und Ausblenden von Zeilen gemäß "viewLvl"

    Das ist ein klar abgegrenztes Modul. Und damit sollte auch das in Ordnung sein.

    Ich nehme aus der Diskussion bis hierhin mit, dass die Anbindung einer List(of Irgendwas) an eine Datagridview über eine BindingSource zwar möglich ist ... aber dass die Sache mit dem "Filter" nicht funktioniert.

    Und deshalb werde ich jetzt dem Rat von EDR und Takafusa folgen und einen Dataset verwenden. Mal sehen, ob die Sache dann besser funzt. Ich gebe natürlich eine Rückmeldung, ob es funktioniert. :)

    Wie immer herzlichen Dank an alle Ratgeber !

    LG
    Peter

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

    Und das kann ich nicht so stehen lassen.
    Mach mal bitte einen Screenshot von Deinem DGV, bevor Du mit LINQ (.Where) filterst und danach. Und auch den Code, bei dem Du die BindingSource1.DataSource festlegst. Irgendwie hab ich das Gefühl, dass da unterschiedliche Datentypen dann drin sind und deshalb die Anzeige falsch ist.
    Denn ich habe z.B.:

    VB.NET-Quellcode

    1. Public Class Form1
    2. Public Class Transponder
    3. Property Name As String
    4. Property Level As Integer
    5. End Class
    6. Public ReadOnly TransponderList As New List(Of Transponder)
    7. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    8. BindingSource1.DataSource = TransponderList.Where(Function(x) x.Level > 2)
    9. End Sub
    10. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    11. BindingSource1.DataSource = TransponderList
    12. BindingSource1.Add(New Transponder With {.Name = "ARD", .Level = 1})
    13. BindingSource1.Add(New Transponder With {.Name = "ZDF", .Level = 2})
    14. BindingSource1.Add(New Transponder With {.Name = "NDR", .Level = 3})
    15. BindingSource1.Add(New Transponder With {.Name = "WDR", .Level = 4})
    16. End Sub
    17. End Class

    Ergebnis im Anhang.
    Bilder
    • VorUndNachFilter.png

      8,52 kB, 725×246, 54 mal angesehen
    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.
    Hmm ... die Screenshots habe ich doch im Post #27 schon angehängt ! Und auch das von mir verwendete Coding, das zu dem Display führt ...

    Sollte mich freuen, wenn das doch noch zu korrigieren sein sollte. Denn ich denke für so ein einfaches Problem brauche ich die Funktionalität eines "Dataset" nicht !

    LG
    Peter

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

    Peter329 schrieb:

    Man sieht wie du einen Dataset definierst und den dann in der FormLoad Prozedur an eine DGV bindest.


    Ich habe nicht selbst gebunden, in dem Moment wo ich aus den DatenQuellen das DataGridView auf das Form ziehe und ablege, wird die ganze Bindung automatisch generiert, im Form_Load füttere ich nur. Wobei ich hier ein DataSet bevorzugen würde, weil: Du kannst deine Arbeitsschritte in einer XML speichern und wieder auslesen mit jeweils 1 Zeile Code, DataSet.WriteXML(string filename) und DataSet.ReadXML(string filename);

    VaporiZed schrieb:

    Und auch den Code, bei dem Du die BindingSource1.DataSource festlegst.
    Bei Deinen Screenshots sehe ich unterschiedliche Spaltenköpfe. Also ist auch ein unterschiedlicher Datentyp vor und nach dem Filtern im DGV. Daher brauch ich die BS-Befüllungscodes. Ich tippe mal darauf, dass das nach dem Filtern auch der aus Post#27 ist. Aber der für die Erstfestlegung von BindingSource1.DataSource?
    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.
    Also ... jetzt hab ich mal ein Testprojekt erstellt, mit dem ich den Sachverhalt demonstrieren kann. (s. TestBinding.zip)

    Einen kleinen "tll File" habe ich auch angehängt. (s. Test.txt, den man in den Downloads Ordner stellen sollte.

    Wenn man das Projekt startet, erhält man ein "vernünftiges" Display. (s. Screenshot 1)

    Wenn man nun die Reload Taste drückt, dann erhalt man "Schrott" (s. Screenshot 2)

    Die Anweisung

    VB.NET-Quellcode

    1. Private Sub cmdReload_Click(sender As Object, e As EventArgs) Handles cmdReload.Click
    2. BindingSource1.DataSource = TransponderList.Where(Function(x) x.Lvl <= viewLvl)
    3. End Sub


    funktioniert so nicht.

    Wer weiß, was ich da falsch mache ?

    LG
    Peter
    Bilder
    • s 2020-08-21 13-58-215.jpg

      16,93 kB, 851×773, 45 mal angesehen
    • s 2020-08-21 13-58-124.jpg

      94,96 kB, 851×773, 45 mal angesehen
    Dateien
    • Test.txt

      (3,85 kB, 49 mal heruntergeladen, zuletzt: )
    • TestBinding.zip

      (80,12 kB, 56 mal heruntergeladen, zuletzt: )
    Hast du mal geschaut welchen Wert viewLvl hat? (in cmdReload_Click)
    ->Nein!
    Hast du mal geschaut ob etwas in der TransponderListe ist? (in cmdReload_Click)
    ->Nein!

    Sorry for the rudeness :D

    Denn dann wäre dir aufgefallen, dass viewLvl immer = 0 ist und auch der Count der Liste. Man muss auch mal debuggen üben indem man sich den Inhalt der Variablen anschaut. ;)



    Warum war nichts in der Liste?
    ->Weil du sie vorher nicht als datasource für die Bindingsource festgelegt hast. (bevor du zur BindingSource addest)

    Was ich jetzt gemacht habe:
    ->Ich habe viewLvl = newLvl in cmdLevelDown_Click hinzugefügt.
    ->Die Liste TransponderList ist jetzt keine Property mehr(in diesem Fall Unsinn)
    ->BindingSource1.DataSource = TransponderList in LoadEditData hinzugefügt

    Nun geht es.
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System.IO
    2. Imports System.Text
    3. Public Class Form1
    4. Dim EditData As String = ""
    5. Dim viewLvl As Integer = 0
    6. Dim indent As String = ""
    7. Dim increment As String = " "
    8. Dim len As Integer = increment.Length
    9. Public TransponderList As New List(Of Transponder)
    10. Dim BindingSource1 As New BindingSource
    11. Public NewLine As String = Convert.ToChar(13) & Convert.ToChar(10)
    12. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    13. EditData = ReadTllFile("C:\Users\Takafusa\Desktop\Test.txt")
    14. LoadEditData()
    15. End Sub
    16. Private Function ReadTllFile(_myFile As String) As String
    17. If Not File.Exists(_myFile) Then
    18. MessageBox.Show("File does not exist.")
    19. Return ""
    20. End If
    21. Try
    22. Return File.ReadAllText(_myFile, Encoding.UTF8)
    23. Catch ex As Exception
    24. MessageBox.Show("Error reading file." & NewLine & _myFile & NewLine & ex.Message)
    25. Return ""
    26. End Try
    27. End Function
    28. Private Function LoadEditData() As Boolean
    29. BindingSource1.DataSource = TransponderList
    30. dgvTransponder.DataSource = Nothing
    31. dgvTransponder.Rows.Clear()
    32. Dim cnt As Integer = 0
    33. Dim sr As StringReader = Nothing
    34. Try
    35. sr = New StringReader(EditData)
    36. Dim line = sr.ReadLine() 'Read ahead
    37. Dim lvl As Integer
    38. Do While (Not line Is Nothing) 'Process all lines
    39. cnt += 1
    40. If cnt < 3 OrElse line.StartsWith("</") Then 'XML header or end token
    41. lvl = UpdateIndentation(-1) 'Reduce indentation
    42. If viewLvl = 0 OrElse lvl <= viewLvl Then
    43. BindingSource1.Add(New Transponder With {.Seq = cnt, .Lvl = lvl, .Tag = indent & line})
    44. End If
    45. ElseIf line.StartsWith("<") Then 'Start token
    46. If viewLvl = 0 OrElse lvl <= viewLvl Then
    47. BindingSource1.Add(New Transponder With {.Seq = cnt, .Lvl = lvl, .Tag = indent & line})
    48. End If
    49. If Not line.Contains("</") Then lvl = UpdateIndentation(+1) 'Increase indentation
    50. End If
    51. line = sr.ReadLine() 'Read next
    52. Loop
    53. Catch ex As Exception
    54. MessageBox.Show("Error loading data into DGV." & NewLine & ex.Message)
    55. Return False
    56. Finally
    57. If Not sr Is Nothing Then sr.Close()
    58. End Try
    59. dgvTransponder.DataSource = BindingSource1
    60. dgvTransponder.Columns("Seq").Width = 80
    61. dgvTransponder.Columns("Seq").DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight
    62. dgvTransponder.Columns("Lvl").DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight
    63. dgvTransponder.Columns("Lvl").Width = 40
    64. dgvTransponder.Columns("Tag").AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill
    65. dgvTransponder.ClearSelection()
    66. txtViewLvl.ForeColor = SystemColors.ControlText
    67. Return True
    68. End Function
    69. Private Function UpdateIndentation(direction As Integer) As Integer
    70. If direction > 0 Then
    71. indent &= increment
    72. Else
    73. If indent.Length >= len Then indent = indent.Substring(0, indent.Length - len)
    74. End If
    75. Dim myLvl = CInt(indent.Length / increment.Length)
    76. Return myLvl
    77. End Function
    78. Private Sub cmdLevelDown_Click(sender As Object, e As EventArgs) Handles cmdLevelDown.Click, cmdLevelUp.Click
    79. Dim myButton = DirectCast(sender, Button)
    80. Dim direction = If(myButton Is cmdLevelDown, -1, +1)
    81. Dim newLvl = CInt(txtViewLvl.Text)
    82. newLvl += direction
    83. If newLvl < 0 Then newLvl = 0
    84. txtViewLvl.Text = newLvl.ToString("n0")
    85. viewLvl = newLvl
    86. txtViewLvl.ForeColor = If(viewLvl <> newLvl, Color.Red, SystemColors.ControlText)
    87. End Sub
    88. Private Sub cmdReload_Click(sender As Object, e As EventArgs) Handles cmdReload.Click
    89. BindingSource1.DataSource = TransponderList.Where(Function(x) x.Lvl <= viewLvl)
    90. End Sub
    91. End Class
    92. Public Class Transponder
    93. Property Seq As Integer
    94. Property Lvl As Integer
    95. Property Tag As String
    96. End Class



    Aber ich glaube hier kannste dir die BindingSource sparen und die Liste direkt ans DataGridView binden.

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

    Wow ... du hast natürlich vollkommen Recht: das mit

    VB.NET-Quellcode

    1. Public Property TransponderList As New List(Of Transponder)


    konnte gar nicht funktionieren!

    Für das "Filtering" muss also eine TransponderLIst anlegen und später befüllen und als Datasource zuweisen:

    VB.NET-Quellcode

    1. Public TransponderList As New List(Of Transponder)


    Und die wird dann als DataSource mit "Filter" zugewiesen.

    VB.NET-Quellcode

    1. Private Sub cmdReload_Click(sender As Object, e As EventArgs) Handles cmdReload.Click
    2. viewLvl = CInt(txtViewLvl.Text)
    3. BindingSource1.DataSource = TransponderList.Where(Function(x) x.Lvl <= viewLvl)
    4. End Sub


    In dieses Command gehört übrigens auch die Sache mit dem "viewLvl" hinein ... das hatte ich beim Erstellen des Testprojekts verschlabbert !

    Die Sache funktioniert jetzt ! Und ich bin hellauf begeistert. Meinen Pessimus von vorhin ziehe ich zurück ! So kann man eine List(of Dingenskirchen) an eine DGV mit Filter anschließen. Jetzt wo ich das sehe, scheint alles ganz einfach ... der Nebel hat sich endlich gelichtet. :)

    Also: Problem gelöst ! Ich danke allen Ratgebern für ihre unendliche Geduld und Nachsicht ... vor allem Takafusa für seinen "finalen Lösungsschuss" ! Das war schon überaus nett, dass ihr euch so eingehend mit meinem Problem auseinander gesetzt habt. Mein Tag ist gerettet !

    LG
    Peter

    Peter329 schrieb:

    konnte gar nicht funktionieren!


    Doch.

    Hier aber unsinnig. Hast du jetzt ein Feld in einer Klasse, wie z.B. ein String, und du willst diesen String binden, dann brauchst du eine Property, darum sind in der Klasse Transponder auch Properties drin anstatt "einfache" Variablen. Willst du z.B. bei Wertänderung reagieren, kannst du dies im Setter machen.

    Um es nochmal genauer zu sagen, wir binden nicht die Liste, die ist nur die Datasource, die Bindung "ziehlt" auf die Properies in die Klasse Transponder.

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