ComboBox an Enum Property binden

  • VB.NET

Es gibt 21 Antworten in diesem Thema. Der letzte Beitrag () ist von cl10k.

    ComboBox an Enum Property binden

    Hallo,

    ich möchte die Property einer Datenklasse an eine Combobox binden. Aber irgendwie klappt es noch nicht so ganz...

    Meine Datenklasse

    VB.NET-Quellcode

    1. Partial Public Class pcls_Vehicle
    2. Inherits cls_DataBaseClass 'Implementiert u.a. Inotify für Databinding
    3. Public Enum EngineTypeList
    4. BipropellantEngine
    5. MonopropellantEngine
    6. SolidpropellantEngine
    7. End Enum
    8. Private _EngineType As EngineTypeList
    9. Public Property EngineType() As EngineTypeList
    10. Get
    11. Return _EngineType
    12. End Get
    13. Set(ByVal value As EngineTypeList)
    14. _EngineType = value
    15. NotifyPropertyChanged("EngineType")
    16. End Set
    17. End Property
    18. 'viele andere Properties
    19. End Class


    Normaler Weise, binde ich alle meine Controls nach dem üblichen Muster, bei der Combobox klappt es aber nicht...

    VB.NET-Quellcode

    1. Public Class frm_Main
    2. Public Vehicle As New pcls_Vehicle
    3. Private Sub frm_Main_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Me.Load
    4. ComboBox1.DataBindings.Add(New Binding("DataSource", Vehicle, "Enginetype", True, DataSourceUpdateMode.OnPropertyChanged))
    5. End Sub
    6. End Class


    Mir ist auch klar, wie ich allgemein eine Enum an eine Combobox binde:

    VB.NET-Quellcode

    1. ComboBox1.DataSource = [Enum].GetValues(GetType(pcls_Vehicle.EngineTypeList))

    -> bringt mich hier erstmal nicht weiter...

    Diese Kombination aus einer Enum-Property und der Combobox durchblicke ich nicht so richtig. (Comboboxen an einfache Typen wie String oder Integer zu binden bekomme ich hin...)

    Wie macht man es hier richtig?

    lg

    cl10k schrieb:

    Comboboxen an einfache Typen wie String oder Integer zu binden bekomme ich hin...
    Das Problem ist halt, dass das Enum kein Integer ist, sondern ein Enum.
    Mach Dir eine Readonly Property, die den Integer-Wert der Binding-Source in ein Enum konvertiert.
    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!

    cl10k schrieb:

    Normaler Weise, binde ich alle meine Controls nach dem üblichen Muster, bei der Combobox klappt es aber nicht...

    Bei Combos weist man die Datasource zu - da wird doch kein Binding dran gesetzt! Hast du je ein Binding auf die Datasource-Property eines Controls gesetzt? Nein, man bindet an Label."Text", NumericUpdown."Value", DatetimePicker."Value" etc.
    Und bei Combos bindet man eben an Combobox."SelectedValue".

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

    Danke euch beiden! Ich habe eine Weile gebraucht, weil ich hier tatsächlich noch eine etwas kompliziertere Situation habe, bei der die Combobox, eine Datenklasse, ein DataSet, ein TabControl und ein Panel zusammen arbeiten sollen. Jetzt funktioniert es einigermaßen passabel - der Sachverhalt ist sicherlich etwas zu speziell, als dass es sich lohnt hier im Detail weiter darauf einzugehen. (Im Endeffekt bin ich Rods Vorschlag gefolgt und habe an Combobox.SelectedIndex gebunden)

    @EDR: Ja logisch - manchmal sehe ich den Wald vor lauter Bäumen nicht...


    Aber ein kleines Problem bleibt dann doch noch:

    Ich möchte mittels Combobox zwischen den Tabpages eines TabControls umschalten.

    Bisher befülle ich die Combobox ganz primitiv:

    VB.NET-Quellcode

    1. For i = 0 To BoundTabControl.TabCount - 1
    2. CmbBx_Input.Items.Add(BoundTabControl.TabPages(i).Text)
    3. Next

    und dann auf das Combobox.SelectedIndexChanged-Event reagieren...



    Man kann aber auch wunderbar einfach die Tabpages-Collection an die DataSource der Combobox binden, nur wie kann man nun Value- und Displaymember an die Name- bzw. Texteigenschaft der Tabpages binden?

    VB.NET-Quellcode

    1. CmbBx_Input.DataSource = BoundTabControl.TabPages 'klappt prima, Combobox wird auch befüllt, aber die Einträge haben keinen Text..
    2. CmbBx_Input.DisplayMember = BoundTabControl.TabPages.Text 'geht natürlich nicht - aber wie macht man es richtig?
    3. CmbBx_Input.ValueMember = BoundTabControl.TabPages.Name 'geht natürlich nicht - aber wie macht man es richtig?

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

    cl10k schrieb:

    Man kann aber auch wunderbar einfach die Tabpages-Collection an die DataSource der Combobox binden, nur wie kann man nun Value- und Displaymember an die Name- bzw. Texteigenschaft der Tabpages binden?

    Logisch denken - gugge Code:

    VB.NET-Quellcode

    1. With ListBox1
    2. .DataSource = TabControl1.TabPages
    Was für Elemente sind nun in der Listbox drin - welchen Datentyp haben die?
    Wie heißt die Property, dieser Elemente, die angezeigt werden soll?
    Welche ListBox-Property willst du an welche TabControl-Property binden?
    Wie ist die Syntax fürs Adden eines Bindings? Insbesondere mit DataSourceUpdatemode.OnPropertyChanged?
    Spoiler anzeigen

    VB.NET-Quellcode

    1. With ListBox1
    2. .DataSource = TabControl1.TabPages
    3. .DisplayMember = "Text"
    4. .DataBindings.Add("SelectedIndex", TabControl1, "SelectedIndex", False, DataSourceUpdateMode.OnPropertyChanged)
    5. End With
    (Listbox und Combobox sind dasselbe - haben dieselbe Basisklasse)
    Moin EDR,

    arrggghhh so hatte ich es sogar versucht... Allerdings gleich in einem Rutsch:

    VB.NET-Quellcode

    1. CmbBx_Input.DataSource = BoundTabControl.TabPages
    2. CmbBx_Input.DisplayMember = "Text"
    3. CmbBx_Input.ValueMember = "Name"


    "Text" akzeptiert die Combobox als Displaymember ohne Probleme (so wie du es oben gezeigt) hast. Aber "Name" widerrum nicht? Dabei ist Combobox.Valuemember ein String und die Name-Eigenschaft der Tabpage ebenfalls !?

    -> "An den neuen Wertemember kann nicht gebunden werden"

    -------

    Leider kann ich TabControl.SelectedIndex und Combobox.SelectedIndex nicht aneinander binden, weil an der Combobox bereits ein Integer-Index einer Datenklasse hängt (das ist aber ok so) - das SelectedIndexChanged-Event tut seinen Dienst genauso gut, zumal die Combobox ein UserControl ist und die Funktionalität schön kapselt ohne mir mein frm_Main vollzumüllen...
    Mein Code ist aber wesentlich anners als deiner. Bei mir ist von ValueMember keine Rede. Ich fürchte, über die gestellten Fragen hast du keine Minute nachgedacht :(

    Ansonsten: ist normal eh besser, an eine gemeinsame Datenklasse zu binden, um 2 Controls zu synchronisieren. Das geht sogar im Designer.
    :huh:

    In der Combobox befinden sich nun Tabpages. Anzeigen lassen möchte ich die Text-Property und als Value die Name-Property der Tabpages!?

    Unser Code unterscheidet sich doch nur insofern, als dass du die Combobox über das with-Statement gleich mit den nötigen Werten initialisierst (korrigier mich ggf.)

    Wenn ich an die Text-Property rankomme, wieso dann nicht an die Name Property?


    Davon abgesehen, ist mein Ansatz, wie bereits geschrieben, etwas wacklig. Ich nutze im Endeffekt einen Integer Index um alle Teile zusammen zu halten.

    Ich habe:

    - ein TabControl mit den Tabpages: EngineTypA, EngineTypB, EngineTypC.
    - eine Combobox zum Umschalten der Tabpages (per Combobox.SelectedIndexChangedEvent)
    - eine Datenklasse mit einer Integereigenschaft EngineType welche an Combobox.SelectedIndex per DataBinding hängt
    ich hoffe, es ist eine ordentliche Datenklasse (also die INotifyPropertyChanged implementiert).
    Dann kannst du TabControl.SelectedIndex ebenso dran binden, wie du Combobox.SelectedIndex dran gebunden hast.

    Hast du meinen Code eigentlich mal ausprobiert? Das ist ja kein Act, eben mal ein TC und eine Listbox aufs Form zu schmeißen.
    Ja, die Datenklasse (DK) implementiert INotify. Um das TabControl auch noch an die DK zu binden, bräuchte ich ja noch eine weitere IntegerProperty - DataBinding geht doch nur immer 1:1...?! Da ziehe ich es vor, dass die Datenklassen sauber bleiben und meine UserControls diese Funktionalität unter sich ausmachen.

    Diese kompakte Schreibweise mit With kannte ich bereits - hatte es aber wieder vergessen. (oder willst du auf etwas anderes hinaus?)

    cl10k schrieb:

    Um das TabControl auch noch an die DK zu binden, bräuchte ich ja noch eine weitere IntegerProperty
    Wer sagt das?

    Ich mach ja selten mit selbstgebastelten Datenklassen, aber bei BindingSources mach ich das oft, dass ich mehrere Controls an dieselbe BindingSource binde - dadurch sind die Controls dann miteinander synchronisiert, und das ist hier doch der erwünschte Effekt.




    Ich wollte auf garnix hinaus, ausser, dass du mein Code einfach mal ausprobierst.
    Moinsen,

    ich möchte nochmal nachhaken zum Thema ComboBox mit DataTable befüllen.

    Das Konzept eine Bindingsource zu nutzen und dann diese dann an die Combobox zu binden ist mir leidlich klar.

    Was aber befüllt man mit Combobobx.Items.Add? In welcher Form werden diese Daten denn gespeichert?


    Ganz konkret möchte ich eine Combobox mit den Spaltennamen eines DataTables befüllen.

    Stackoverflow bringt ein paar Ergebnisse zu diesem Thema, allerdings nutzen diese allesamt eine Schleife mit Items.Add...

    VB.NET-Quellcode

    1. For i = 0 To BoundDataTable.Columns.Count - 1
    2. CmbBx_Sort.Items.Add(BoundDataTable.Columns(i).ColumnName)
    3. Next
    (das funktioniert natürlich, aber mir stehen somit nur die ColumnNames zur Verfügung)


    Ich würde aber gern den Weg über eine DataSource gehen, damit ich als Displaymember Column.Caption und als Valuemember Column.Name nutzen kann - allerdings ohne Erfolg

    VB.NET-Quellcode

    1. CmbBx_Input.DataSource = BoundDataTable.Columns
    2. CmbBx_Input.DisplayMember = "Caption"
    3. CmbBx_Input.ValueMember = "Name"


    Ich habe schon zig Variationen durchprobiert, keine hat funktioniert...

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

    Hallo,

    ich habs durchprobiert und sitze immer noch auf dem Trockenen.


    Direkt an die DataColumnsCollection binden geht nicht -> "Das komplexe DataBinding akzeptiert als Datenquelle entweder IList oder IListsource"

    Die MS Foren sagen dazu: DataColumnsCollection implementiert nur ICollection und nicht IList (seufz) - man soll per Item (Index oder Name) zugreifen - das bringt mich wieder zur Schleife, die ich loswerden will.

    Also deine zweite Idee mit dem Array durchprobiert. dt.Columns hat keine toArray Methode, also von Hand probiert:

    VB.NET-Quellcode

    1. Dim tmp As DataColumn() = New DataColumn(BoundDataTable.Columns.Count - 1) {}
    2. BoundDataTable.Columns.CopyTo(tmp, 0)
    3. CmbBx_Input.DataSource = tmp
    4. CmbBx_Input.DisplayMember = "ColumnName"
    5. CmbBx_Input.ValueMember = "Caption"


    Der Debugger zeigt mir für tmp zwar ein Array von Datacolumns an und diese besitzen dann jeweils auch eine Caption und ColumnName Property jedoch kriege ich "An den neuen Anzeigenmember kann nicht gebunden werden"

    Doppelseufz und Verzweiflung ;(

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

    hmm.
    da war doch einfach die Logik mittte tabpages 1:1 auf mitte DataColumns zu übertragen:

    VB.NET-Quellcode

    1. With ListBox1
    2. .DataSource = TestDataDts.TestTable.Columns.Cast(Of DataColumn).ToArray
    3. .DisplayMember = "ColumnName"
    4. End With
    Warum fängst du schon wieder mittm ValueMember an, der bei die Tabpages schon gefailt hat?
    Und eiglich müsste auchn annerer Exceptiontext kommen.

    ErfinderDesRades schrieb:

    hmm.
    da war doch einfach die Logik mittte tabpages 1:1 auf mitte DataColumns zu übertragen:

    Ja, aber ohne IList. Wenn man den Sachverhalt nicht richtig drauf hat, ist es verdammt schwer mit MSDN und Co. hier auf ein funktionierendes Ergebnis zu kommen....

    ErfinderDesRades schrieb:

    Warum fängst du schon wieder mittm ValueMember an, der bei die Tabpages schon gefailt hat?

    Weil wir nicht abschließend geklärt hatten, warum sich die ColumnName ohne Probleme an DisplayMember binden lässt, aber Caption nicht gleichzeitig an Displaymember und ColumnName an Valuemember. Mir ist einfach nicht klar warum! Die Typen stimmen doch überein...



    Was ich hier tatsächlich probiere:

    Ein Dataset mit n Tables und ein Form mit n UserControls mit jeweils zwei Comboboxen* und eigener BindingSource. Den UC's gebe ich ich schon im Designer den jeweiligen DataTable als BoundDataTable mit.

    *CmbBx_Input zeigt mir die Daten der ersten Spalte des DataTable an:

    VB.NET-Quellcode

    1. LocalBindingSource.DataSource = BoundDataTable.DataSet
    2. LocalBindingSource.DataMember = BoundDataTable.TableName
    3. With CmbBx_Input
    4. .DataSource = LocalBindingSource
    5. .DisplayMember = BoundDataTable.Columns(0).ColumnName
    6. End With



    *CmbBx_Sort dient der Sortierung von CmbBx_Input und zeigt die ColumnCaptions von BoundDataTable an:

    VB.NET-Quellcode

    1. With CmbBx_Sort
    2. .DataSource = BoundDataTable.Columns.OfType(Of DataColumn).ToArray
    3. .ValueMember = "ColumnName"
    4. .DisplayMember = "Caption"
    5. End With


    Über CmbBx_Sort möchte ich die Sortierreihenfolge von CmbBx_Input bestimmen:

    VB.NET-Quellcode

    1. Sub Test() Handles CmbBx_Sort.SelectedIndexChanged
    2. LocalBindingSource.Sort = CmbBx_Sort.ValueMember & " ASC"
    3. End Sub


    Die ColumnNames haben aber nicht so "schönen" Text, z.B. "SpecificImpulse_vac". Also wollte ich ganz clever sein und in CmbBx_Sort zwar die Captions als Displaymember anzeigen lassen, aber über die Valuemember in Sub Test sortieren.


    Puh - das war jetzt komplett unverständlich, oder? Und ja, mir ist klar, dass das hier ein chaotischer Ansatz ist - ich springe hin und her zwischen BindingSource und Datatable, aber anders habe ich es nicht zum Laufen bekommen ;(
    Bilder
    • Unbenannt.png

      166,99 kB, 1.024×768, 204 mal angesehen

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

    Ist das noch nicht deutlich geworden? ValueMember ist hier nicht relevant.
    ValueMember brauchst du hier nicht, also verwende es nicht, dann wirft das auch keinen Fehler.
    ValueMember bewirkt intern ein Databinding, sodass bei Combobox.SelectedValue was anneres bei rauskommt als bei Combobox.SelectedItem.
    Aber beides brauchst du hier nicht, das ist hier nicht relevant.
    Also verwende es besser auch nicht.
    Tatsächlich weiß ich auch nicht, warum da nun ausgerechnet ein Fehler kommt - nach meinem Verständnis bräuchte er auch nicht zu kommen. Probiert habichs nicht, weil - wie gesagt: Ist hier nicht relevant.

    Mir scheint, du willst anhand des Combo.SelectedIndex eine DataColumn-Spalte auswählen, nach der sortiert wern soll.

    (offTopic: Warum klickst du dann nicht einfach auf den Spaltenkopf im DGV?)

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