none
vb .NET ComboBox populated from Dictionary holding a value from a Dataset RRS feed

  • Frage

  • This is a form to maintain user privileges in a WinForm application based on MySQL. Each user has attributes e.g. name, position, entry date, etc. and also access privileges to three areas of the application: to "user maintenance", "supplier maintenance" and "order administration". Access privileges can be one of "no access", "read-only", "modify". They are kept in a Dictionary object. The user data are stored in a table named "User". Among others there is a field for each access privilege of type SByte.

    On the form there are ComboBoxes for each area of the application where an access privilege needs to be assigned. The current access privileges are in the user record in the database table.

    Public Class _000Test
    
    Private Enum AccessPrivilege
        no_access = 0
        read_only = 1
        modify = 2
    End Enum
    
    Private AccessList As New Dictionary(Of Integer, String) From _
        {{AccessPrivilege.no_access, "kein Zutritt"}, _
         {AccessPrivilege.read_only, "darf lesen"}, _
         {AccessPrivilege.modify, "darf bearbeiten"}}
    
    Private Sub _000Test_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    
        OrderAdminComboBox.DataSource = New BindingSource(AccessList, Nothing)
        OrderAdminComboBox.ValueMember = "Key"
        OrderAdminComboBox.DisplayMember = "Value"
        OrderAdminComboBox.DataBindings _
            .Add(New System.Windows.Forms.Binding("SelectedValue", UserBindingSource, "OrderAdmin", True))
    
        SupplierAdminComboBox.DataSource = New BindingSource(AccessList, Nothing)
        SupplierMaintComboBox.ValueMember = "Key"
        SupplierMaintComboBox.DisplayMember = "Value"
        SupplierMaintComboBox.DataBindings _
            .Add(New System.Windows.Forms.Binding("SelectedValue", UserBindingSource, "SupplierMaint", False))
    
        OrderAdminComboBox.DataSource = New BindingSource(AccessList, Nothing)
        OrderAdminComboBox.ValueMember = "Key"
        OrderAdminComboBox.DisplayMember = "Value"
        OrderAdminComboBox.DataBindings _
            .Add(New System.Windows.Forms.Binding("SelectedValue", UserBindingSource, "OrderAdmin", True))
    
        Me.UserTableAdapter.Fill(Me.Bringadb_AppDS.User)
    
    End Sub

    The problem I have is that the comboboxes do not work. No matter what the values in the database record in the table are the combobox would display 0 - "no access". I also had the access privilege values in a table and set the ComboBox-DataSource property to it - same result.

    Any hints? Thanks in advance.



    Freitag, 10. Juli 2015 11:48

Antworten

  • Hi Peter,
    die Typsicherheit sollte immer beachtet werden. Die Konvertierung im Format-Ereignis ist nicht notwendig, wenn sofort die richtigen Typen angewendet werden. Ich hatte in Deinem Ausgangsposting das "SByte" übersehen. Hier mal eine kleine Demo, wie es auch ohne Konvertierung funktioniert:

    Public Class Form1
    
      Dim UserBindingSource As BindingSource
    
      Private Sub Form8_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Me.Width = 400
        ' Daten Laden
        LoadData()
        ' Grid für Anzeige und Navigation
        Dim dgv As New DataGridView With {.DataSource = UserBindingSource, .Dock = DockStyle.Fill}
        ' ComboBox
        Dim cb As New ComboBox With {.Dock = DockStyle.Right}
        With cb
          .DataSource = New BindingSource(AccessList, Nothing)
          .DisplayMember = "Value"
          .ValueMember = "Key"
          .DataBindings.Add("SelectedValue", UserBindingSource, "OrderAdmin")
        End With
        ' Oberfläche laden
        Me.Controls.AddRange(New Control() {dgv, cb})
    
      End Sub
    
      Private Sub LoadData()
        Dim dt As New DataTable
        With dt
          With .Columns
            .Add("ID", GetType(Integer))
            .Add("OrderAdmin", GetType(SByte))
          End With
          For i = 1 To 9
            .Rows.Add(i, 0)
          Next
        End With
        UserBindingSource = New BindingSource(dt, Nothing)
      End Sub
    
      Private Enum AccessPrivilege
        no_access = 0
        read_only = 1
        modify = 2
      End Enum
    
      Private AccessList As New Dictionary(Of SByte, String) From _
          {{CType(AccessPrivilege.no_access, SByte), "kein Zutritt"}, _
           {CType(AccessPrivilege.read_only, SByte), "darf lesen"}, _
           {CType(AccessPrivilege.modify, SByte), "darf bearbeiten"}}
    
    End Class

     

    --
    Viele Grüsse
    Peter Fleischer (MVP, Partner)
    Meine Homepage mit Tipps und Tricks

    • Als Antwort markiert Peter_SkAt Samstag, 11. Juli 2015 13:17
    Samstag, 11. Juli 2015 05:14
  • Entwarnung, ich habe die Lösung auf Stackoverflow erhalten und poste sie auch hier, um sinnlose Suche zu ersparen. Das Problem war offensichtlich, dass intern keine Konversion zwischen den Datentypen SByte aus der Datenbankzeile und dem Integer im ValueMember der ComboBox funktioniert. Die Lösung ist ein Handler, der die Konversion durchführt:

    Dim bindAccess As New Binding("SelectedValue", UserBindingSource, "OrderAdmin", True))
    AddHandler bindAccess.Format, Sub(sender, e) e.Value = Convert.ToInt32(e.Value)
    OrderAdminComboBox.DataBindings.Add(bindAccess)
    

    Mit dieser Konversion funktioniert nach zwei Tagen verzweifeltem Herumprobieren plötzlich alles perfekt. Vielleicht eine kleine Bitte an die MS-Entwickler: solche kapitalen, grundlegenden Dinge, wie dass ein Datentyp nicht passt, nicht einfach stillschweigend übergehen. Eine kleine ArgumentException hätte viel Zeit, Geld und Nerven gespart.

    LG aus WIen
    Peter

    • Als Antwort markiert Peter_SkAt Samstag, 11. Juli 2015 13:17
    Freitag, 10. Juli 2015 20:47

Alle Antworten

  • Hi Peter,
    nimm anstelle eines Wörterbuches eine Liste von Objekte, die zwei Eigenschaften haben, jeweils für die ID und den Langtext.

    --
    Viele Grüsse
    Peter Fleischer (MVP, Partner)
    Meine Homepage mit Tipps und Tricks

    Freitag, 10. Juli 2015 12:37
  • Hallo Peter,

    dies ist ein deutsches Forum, du kannst die Fragen ruhig auf deutsch stellen.

    Otherwise, go to the English forum: https://social.msdn.microsoft.com/Forums/en-us/home


    © 2015 Thomas Roskop
    Germany //  Deutschland

    Freitag, 10. Juli 2015 13:15
  • @Thomas: Pfuh, und das, nachdem ich mich so geplagt hatte mit meinem English, das mehr Pidgin als Oxford ist... :-) Aber im Ernst: ich habe das Problem gestern so auf Stackoverflow gepostet, ohne brauchbares Ergebnis. Und mir ist wurscht, in welcher Sprache ich die Lösung bekomme. Oder meinst du, dass ich den Post übersetzen soll?

    @Peter: genau das dachte ich, dass ein Dictionary ist, nämlich eine (sortierte) Liste von Objekten(Of Interger, String). Aber ich habs davor auch schon versucht mit List(Of KeyValuePair(Of Integer String)). Selbes Ergebnis.

    Aber danke schon einmal, dass ihr da hineingelesen habt.

    LG Peter

    Freitag, 10. Juli 2015 14:10
  • @Peter: Habe das wieder umgeschrieben, sodass die Comboboxen als DataSource eine List(Of KeyVauePair(Of Integer String)) haben - leider keine Änderung. Anscheinend funktioniert das Binding nicht.

    Freitag, 10. Juli 2015 15:19
  • Hi Peter,
    natürlich funktioniert das Binding, wenn man es richtig macht. Voraussetzung dafür ist aber eine Definition des Zieles. Das habe ich aus Deinem Ausgangs-Posting nicht verstanden. Vielleicht beschreibst Du nochmal, was Du erreichen willst/musst.

    --
    Viele Grüsse
    Peter Fleischer (MVP, Partner)
    Meine Homepage mit Tipps und Tricks

    Freitag, 10. Juli 2015 19:09
  • @Peter
    Das Formular ermöglicht die Vergabe von Zugriffprivilegien auf drei Progammbereiche: Benutzerverwaltung, Lieferantenverwaltung und Bestellwesen. Ein Zugriffsprivileg kann einer von 3 Wertern sein : "kein Zugriff", "darf lesen", "darf bearbeiten" (numerisch kodiert als 0, 1, 2). Die ComboBox soll sicher stellen, dass nur Werte aus dieser Domäne eingegeben werden können. Im Datensatz eines Benutzers gibt es die Felder "Name", "Vorname", "SVNr." etc. und ebenfals drei Felder Names "Benutzerverwaltung", "Lieferantenverwaltung" und "Bestellwesen" vom Typ Byte, die die Zugriffsprivilegien des Benutzers enthalten, also Werte zwischen 0 und 2.

    Ich plage mich nun damit, eine Datenbindung zwischen diesen drei Datenfeldern und den drei ComboBoxen auf dem Formular herzustellen. Das ist mir bisher nicht gelungen.

    Es ist kein Problem, die Items der ComboBox (also 0 - kein Zugriff, 1 - darf lesen, 2 - darf bearbeiten) zu befüllen, entweder aus einer Tabelle oder aus einer Liste. Was dann nicht funktioniert, ist das Binding. Hier habe ich verschiedene Verhaltensweisen beobachtet, je nachdem, welche Property der ComboBox referenziert wurde (SelectedValue, SelectedItem, SelectedIndex) und wie das DataBinding angegeben wurde (BindingSource, DataTable).

    Ich hoffe, das war jetzt eine halbwegs verständliche Übersetzung meines englischen Textes. Danke im Voraus für deine Bemühungen.

    LG
    Peter aus Wien
    Freitag, 10. Juli 2015 19:52
  • Entwarnung, ich habe die Lösung auf Stackoverflow erhalten und poste sie auch hier, um sinnlose Suche zu ersparen. Das Problem war offensichtlich, dass intern keine Konversion zwischen den Datentypen SByte aus der Datenbankzeile und dem Integer im ValueMember der ComboBox funktioniert. Die Lösung ist ein Handler, der die Konversion durchführt:

    Dim bindAccess As New Binding("SelectedValue", UserBindingSource, "OrderAdmin", True))
    AddHandler bindAccess.Format, Sub(sender, e) e.Value = Convert.ToInt32(e.Value)
    OrderAdminComboBox.DataBindings.Add(bindAccess)
    

    Mit dieser Konversion funktioniert nach zwei Tagen verzweifeltem Herumprobieren plötzlich alles perfekt. Vielleicht eine kleine Bitte an die MS-Entwickler: solche kapitalen, grundlegenden Dinge, wie dass ein Datentyp nicht passt, nicht einfach stillschweigend übergehen. Eine kleine ArgumentException hätte viel Zeit, Geld und Nerven gespart.

    LG aus WIen
    Peter

    • Als Antwort markiert Peter_SkAt Samstag, 11. Juli 2015 13:17
    Freitag, 10. Juli 2015 20:47
  • Hi Peter,
    die Typsicherheit sollte immer beachtet werden. Die Konvertierung im Format-Ereignis ist nicht notwendig, wenn sofort die richtigen Typen angewendet werden. Ich hatte in Deinem Ausgangsposting das "SByte" übersehen. Hier mal eine kleine Demo, wie es auch ohne Konvertierung funktioniert:

    Public Class Form1
    
      Dim UserBindingSource As BindingSource
    
      Private Sub Form8_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Me.Width = 400
        ' Daten Laden
        LoadData()
        ' Grid für Anzeige und Navigation
        Dim dgv As New DataGridView With {.DataSource = UserBindingSource, .Dock = DockStyle.Fill}
        ' ComboBox
        Dim cb As New ComboBox With {.Dock = DockStyle.Right}
        With cb
          .DataSource = New BindingSource(AccessList, Nothing)
          .DisplayMember = "Value"
          .ValueMember = "Key"
          .DataBindings.Add("SelectedValue", UserBindingSource, "OrderAdmin")
        End With
        ' Oberfläche laden
        Me.Controls.AddRange(New Control() {dgv, cb})
    
      End Sub
    
      Private Sub LoadData()
        Dim dt As New DataTable
        With dt
          With .Columns
            .Add("ID", GetType(Integer))
            .Add("OrderAdmin", GetType(SByte))
          End With
          For i = 1 To 9
            .Rows.Add(i, 0)
          Next
        End With
        UserBindingSource = New BindingSource(dt, Nothing)
      End Sub
    
      Private Enum AccessPrivilege
        no_access = 0
        read_only = 1
        modify = 2
      End Enum
    
      Private AccessList As New Dictionary(Of SByte, String) From _
          {{CType(AccessPrivilege.no_access, SByte), "kein Zutritt"}, _
           {CType(AccessPrivilege.read_only, SByte), "darf lesen"}, _
           {CType(AccessPrivilege.modify, SByte), "darf bearbeiten"}}
    
    End Class

     

    --
    Viele Grüsse
    Peter Fleischer (MVP, Partner)
    Meine Homepage mit Tipps und Tricks

    • Als Antwort markiert Peter_SkAt Samstag, 11. Juli 2015 13:17
    Samstag, 11. Juli 2015 05:14
  • Hi Peter,
    die Typsicherheit sollte immer beachtet werden.

    Hm, ich bin 63 Jahre alt, habe mit PDP- und VAX-Assembler begonnen, mit lustigen Sprachen wie Fortran IV, Cobol und dem Ur-C weitergemacht, bis zur ersten objektorientierten Ingres-Entwicklungsumgebung. Dort die Datentypen nicht strengstens zu beachten war der direkte Weg in den Abgrund.

    Ich habe jetzt daher noch einmal in der MS-Doku im Internet nachgesehen. Bei der ComboBox (https://msdn.microsoft.com/de-de/library/system.windows.forms.combobox%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396) wird bei ValueMember gesagt: "Ruft den Pfad zur Eigenschaft ab, die als tatsächlicher Wert für die Elemente im ListControl verwendet werden soll, oder legt diese fest. (Von ListControl geerbt.)"

    Wenn man diesen Link zur ListControl folgt (https://msdn.microsoft.com/de-de/library/system.windows.forms.listcontrol%28v=vs.110%29.aspx), erfährt man: "Ruft den Pfad zur Eigenschaft ab, die als tatsächlicher Wert für die Elemente im ListControl verwendet werden soll, oder legt diese fest."

    Wenn man dort dann auf ValueMember klickt (https://msdn.microsoft.com/de-de/library/system.windows.forms.listcontrol.valuemember%28v=vs.110%29.aspx), dann steht auf der Dokumentationsseite für  ValueMember folgenden Text: "Public Property ValueMember As String". Also weder SByte noch Integer!? Heißt das, die Runtime kann einen Integer-Wert implizit in String konvertieren, einen SByte-Wert jedoch nicht?

    (Ganz abgesehen davon, dass Konversionen innehalb der Integer-Familie in Richtung größer wirklich kein Thema sein dürften, das ging schon im Assembler implizit.)

    Dann also zwei kleine Bitten an MS: 1. wenn der Datentyp so wichtig ist, bitte diesen in die Doku hineinschreiben. 2. wenn der Datentyp eines an das Objekt übergebenen Wertes nicht passt, bitte eine ArgumentException oder sonst einen kleinen Hinweis an den Entwickler ausgeben.

    Vielleicht könnten Sie dies als MVP, Partner an Microsoft weitergeben.

    Nochmals vielen Dank für die Hilfe, ich hoffe, die Sache war für beide Seiten von Nutzen.

    LG
    Peter




    • Bearbeitet Peter_SkAt Samstag, 11. Juli 2015 07:15 Tippfehler
    Samstag, 11. Juli 2015 07:08
  • Hi Peter,
    da ich Dein Alter schon ein paar Jahre hinter mir habe und auch die von Dir genannten Sprachen und Umgebungen aus eigener Erfahrung kenne, kann ich Dich ganz gut verstehen.

    Bereist seit Urzeiten (z.B. VB Classic) führt die Combobox parallel die Anzeige im Vordergrund und einen Referenzwert im Hintergrund. ValueMember erwartet die Zeichenkette, die Bezeichnung des Pfades der Eigenschaft enthält, die den Referenzwert im Hintergrund mitführt. Die Anzeige im Vordergrund wird aus der Eigenschaft gezogen, die als Zeichenkette in der DisplayMember benannt wird.

    Typischerweise nutzt die Combobox für die Anzeige und Auswahl eine Liste von Datenobjekten als Datenquelle. Wenn diese Datenobjekte mehr als eine Eigenschaft ausweisen, kann man die Anzeige und den Referenzwert separat verwalten, indem man der Combobox über DisplayMember und ValueMember die Namen der Eigenschaften dieser Datenobjekte festlegt. Dabei wird nix konvertiert. Konvertiert wird indirekt die Anzeige in der Liste, indem der Inhalt der mit DisplayMember festgelegten Eigenschaft bei Bedarf mit der ToString-Methode für die Anzeige geholt wird. Der Inhalt der mit ValueMember definierten Eigenschaft wird im Hintergrund nur als Objekt unabhängig vom Typ mitgeführt. Da wird nichts konvertiert.

    Durch die Auswahl in der Liste wird das ausgewählte Datenobjekt als SeletectItem bereitgestellt. Zusätzlich wird bei Vorhandensein einer ValueMember der Inhalt der durch ValueMember spezifizierten Eigenschaft als Objekt in der SelectedValue-Eigenschaft der Combobox bereitgestellt. Der Typ dieses Objektes wird durch die entsprechende Eigenschaft des über die DataSource gebundenen Datenobjektes bestimmt. In Deinen ursprünglichen Fall war das Integer aus dem Dictionary AccessList.

    Die Technologie zur Datenbindung mittels Binding-Objekt, die Du bei der Bindung an die SelectedValue-Eigenschaft genutzt hast, hat aus Datenquelle für die Bindung den Inhalt der Eigenschaft "OrderAdmin" geholt und auch wieder abgelegt. Diese Eigenschaft ist vom Type SByte. Beim Auslesen aus "OrderAdmin" soll das SByte einem Integer zugeordnet werden, was nicht auflösbar war ist (narrowing) und deshalb zu keinem Ergebnis führt. Der umgekehrte Weg (widening - Auswahl in der Combobox und Ablage in OrderAdmin) funktioniert dagegen.

    zu 1.: Welcher Datentyp soll denn in die Doku geschrieben werden, wenn jeder Datentyp als SelectedValue zulässig ist?

    zu 2.: Es wäre schön, wenn an allen Stellen unpassende Zuweisungen zur Laufzeit als Fehler ausgewiesen werden würden. Vermutlich wird Microsoft aber an den seit fast 2 Jahrzehnten vorhandenen WindowsForms-Steuerelementen kaum noch etwas ändern. Bei WPF gibt es wenigsten bei unpassenden Bindungen meist einen Eintrag im Output des Visual Studios.


    --
    Viele Grüsse
    Peter Fleischer (MVP, Partner)
    Meine Homepage mit Tipps und Tricks

    Samstag, 11. Juli 2015 08:58
  • Ok, zuersteinmal vielen Dank, Peter, dass du dir an einem Samstag diese Zeit nimmst, die Dinge deppensicher zu erklären. Ich habe beim Programmieren nur die Doku aus dem Internet zur Verfügung und die ist - tja, so wie sie eben ist. MS legt offenbar eher Wert auf Quantität und Formalismus, selten findet man einfache, problemorientierte Doku. Und die vielen Foren, da bin ich nie sicher, lese ich nun Fakten oder die religiöse Überzeugung des jeweiligen Posters...

    Danke jedenfalls und ein schönes Wochenende.

    LG
    Peter


    • Bearbeitet Peter_SkAt Samstag, 11. Juli 2015 09:27
    Samstag, 11. Juli 2015 09:27
  • Hi Peter,
    ist Dein Problem nun gelöst? Bisher ist der Thread noch offen.

    Ich finde die Doku in Form der MSDN schon sehr gut und sehr umfangreich. Hauptproblem bei der Nutzung ist wie bei jeder Software-Doku das Verständnis für den Aufbau der Hilfe. In Zusammenarbeit mit dem Visual Studio und der F1-Taste findet man praktisch zu jedem Detail eine Info. Voraussetzung dafür ist aber, dass man einige Grundkenntnisse hat. Und da haben mir die 1500 Buchseiten der Core Referenz geholfen, die ich vor 14 Jahren in mehreren Monaten Seite für Seite durchgearbeitet hatte.


    --
    Viele Grüsse
    Peter Fleischer (MVP, Partner)
    Meine Homepage mit Tipps und Tricks

    Samstag, 11. Juli 2015 12:21
  • Hallo Peter,

    ja, diese 1500 Seiten dürften mir fehlen, ich habe in den letzten 20 Jahren sesselgefurzt und gemanagt und nicht fachlich gearbeitet. Naja, bald werde ich ja hoffentlich viel Zeit haben, da kann ich das nachholen.

    Und ja, das, beser dieses, Problem ist gelöst, nochmals Danke. Da ich in dem Forum erst ganz wenig gepostet habe, weiß ich nicht, wie man einen Thread schließt (gibts für die Foren auch irgendwo 1500 Seiten? :-)). Bitte um Anleitung oder vielleicht kann das jemand für mich tun.

    Liebe Grüße
    Peter

    Samstag, 11. Juli 2015 12:30
  • Hi Peter,
    Du kannst die Beiträge "als Antwort markieren", die Dir zur Lösung des Problems geholfen haben. Damit hilfst Du auch anderen, die diese Frage auch hatten und damit wissen, was anderen zur Lösung des Problems geholfen hat.

    --
    Viele Grüsse
    Peter Fleischer (MVP, Partner)
    Meine Homepage mit Tipps und Tricks

    Samstag, 11. Juli 2015 12:51