Combobox zur Laufzeit füllen

  • WPF

Es gibt 17 Antworten in diesem Thema. Der letzte Beitrag () ist von Dksksm.

    Combobox zur Laufzeit füllen

    Hallo,

    mit meinem kleinen Projekt bin ich dank @Nofear23m sehr viel weiter gekommen. Danke dafür.
    Nun habe ich aber wieder einen völligen "Hänger".

    Gegeben ist eine List<UsedNodes> usedNodes, die erst zur Laufzeit befüllt wird. Hierbei handelt es sich um eine Aufzählung vorhandener XML-Knoten aus einer Fremd-XML-Datei.
    Die Klasse, die Zugrunfe liegt ist entsprechend eifach gestalltet:

    C#-Quellcode

    1. internal class UsedNodes
    2. {
    3. internal string Name { get; set; }
    4. }


    Nun habe ich leider gar keinen Schimmer, wie ich einer Combobox diese Liste als ItemsSource unterschieben kann. Alle anderen Bindings (Buttons, Texte) im ViewModel funktionieren.
    Bzw. falls meine Bindung stimmen sollte (was ich bezeweifle), dann aktualisiert sie nicht, auch nicht weiter verwunderlich, denn ich weiß gar nicht wie ich das anschubsen kann.

    Im XAML sieht es so aus:

    XML-Quellcode

    1. <ComboBox x:Name="cmbNode" ItemsSource="{Binding usedNodes}" DisplayMemberPath="Name" SelectedValuePath="Name" SelectedValue="{Binding Name}"/>


    Ich brauch mal wieder einen Schubser, sorry dafür.
    Grüße
    Rob

    Dksksm schrieb:

    Bzw. falls meine Bindung stimmen sollte (was ich bezeweifle), dann aktualisiert sie nicht, auch nicht weiter verwunderlich, denn ich weiß gar nicht wie ich das anschubsen kann.

    Wenn ein Binding nicht stimmt siehst du das in der ausgabe. Die ist extrem wichtig. Behalte diese in sachen Binding immer im Auge.

    Wenn die List(Of) erst nach dem setzen des DatenKontext gefüllt wird musst du unbedingt eine ObservableCollection nehmen. die List(Of) hat keinen Benrachrichtgungsmechanismus (INotifyCollectionChanged).

    Ansonsten wäre das ViewModel interessant zu sehen.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Hallo @Nofear23m

    ich habe es hinbekommen :)
    Also das ViewModel ist im Grunde von Dir ;) aus dem letzten Thread.
    Ich habe zugefügt:

    C#-Quellcode

    1. private ObservableCollection<string> _listUsedNodes;
    2. public ObservableCollection<string> ListUsedNodes
    3. {
    4. get
    5. {
    6. return _listUsedNodes;
    7. }
    8. set
    9. {
    10. _listUsedNodes = value;
    11. RaisePropertyChanged();
    12. }
    13. }


    Im XAML wie folgt geändert:

    XML-Quellcode

    1. <ComboBox x:Name="cmbNode" HorizontalAlignment="Right" Margin="0,109,150,0" VerticalAlignment="Top" Width="220">
    2. <ComboBox.ItemsSource>
    3. <Binding Path="ListUsedNodes" BindsDirectlyToSource="True" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged"></Binding>
    4. </ComboBox.ItemsSource>
    5. </ComboBox>


    Dann habe ich im ViewModel noch in der Funktion, die die Veränderung an den Items durchführt zugefügt:

    C#-Quellcode

    1. ListUsedNodes = new ObservableCollection<string>();
    2. foreach (var node in usedNodes)
    3. ListUsedNodes.Add(node.Name);


    Viele Grüße
    Rob
    Hallo

    Sehr gut gemacht. Hier eine vereinfachung:

    Statt:

    XML-Quellcode

    1. <ComboBox x:Name="cmbNode" HorizontalAlignment="Right" Margin="0,109,150,0" VerticalAlignment="Top" Width="220">
    2. <ComboBox.ItemsSource>
    3. <Binding Path="ListUsedNodes" BindsDirectlyToSource="True" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged"></Binding>
    4. </ComboBox.ItemsSource>
    5. </ComboBox>

    einfach:

    XML-Quellcode

    1. <ComboBox x:Name="cmbNode" HorizontalAlignment="Right" Margin="0,109,150,0" VerticalAlignment="Top" Width="220" ItemsSource="{Binding ListUsedNodes, Mode="TwoWay"}"/>


    Und statt:

    C#-Quellcode

    1. ListUsedNodes = new ObservableCollection<string>();
    2. foreach (var node in usedNodes)
    3. ListUsedNodes.Add(node.Name);


    einfach:

    C#-Quellcode

    1. ListUsedNodes = new ObservableCollection<string>(usedNodes.Select(x => x.Name));



    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Hallo @Nofear23m

    Danke für Lob und die Vorschläge zur Änderung. Ja Linq ist nice :)
    Den Vorschlag ein habe ich abgeändert, danke für die Vereinfachung, ist für mich leichter lesbar :)

    XML-Quellcode

    1. <ComboBox x:Name="cmbNode" HorizontalAlignment="Right" Margin="0,109,150,0" VerticalAlignment="Top" Width="220" ItemsSource="{Binding ListUsedNodes, Mode=TwoWay}" SelectedValue="{Binding SelectedNode, Mode=TwoWay}"/>


    Ich habe einen Button Command, der im CanExecute auf die SelectedNode prüft, damit sich ausgewählte Nodes löschen lassen. Bis hierhin geht alles prima.
    Klar, dass nun ein Aber kommt. Ich möchte nach der Auswahl eines Node in einer zweiten Combobox die zu diesem Node gehörenden Attribute anzeigen lassen.
    Hier ist auch "nur" wieder das Problem, dass ich nicht weiß, wie ich das rein mit MVVM Pattern lösen kann.
    Oder muss ich doch das SelectionChanged abbonieren?

    Grüße
    Rob

    Dksksm schrieb:

    Ich möchte nach der Auswahl eines Node in einer zweiten Combobox die zu diesem Node gehörenden Attribute anzeigen lassen.

    Ja, und jetzt kommen wir fast zu dem Problem welches ich mir fast dachte.

    Du hast ja deine usedNodes. Eine Liste von UsedNode. Jetzt hast du eine Collection gemacht wo du die Liste in eine Observable ladest aber auf Strings reduzierst.

    Am besten gehst du einen schritt zurück. Du machst aus deiner List(Of UsedNodes) eine ObservableCollection(Of UsedNodes) anstatt einer ObservableCollection(Of String).
    SelectedNode ist auch vom Typ UsedNode anstatt String. Dann kannst du von überall aus auf SelectedNode binden.

    Ich hoffe das war verständlich.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Hallo @Nofear23m

    na so gut bin ich nicht. Ich versuchs zu verstehen und werde noch dannach googlen, in der Hoffnung dann weiter zu kommen.
    Ich muss gleich auf Reisen gehen und bin erst mal sehr busy. Trotzdem bin ich Glücklich, bis jetzt durchgehalten zu haben.
    Wenn man erst den Fortschritt sieht, und erkennt was man sich an Arbeit alles ersparen kann, ist WPF auch aus der Perspektive ein Segen.
    Ich habe ein sehr ähnliches Projekt in Winforms umgesetzt und kann daher 1:1 vergleichen. Optisch ist es gut, ich hab die Oberfläche schon ein wenig mit MahApps aufgepustet.
    Die Execute ist dennoch größer als bei winforms, und es kommen noch ein paar DLLs dazu. Aber die GUI fühlt sich deutlich fluffiger und geschmeidiger an.

    Danke für Deine stetige Hilfe.
    Gruß
    Rob

    Dksksm schrieb:

    Danke für Deine stetige Hilfe.

    Kein Problem. Dafür ist das Forum ja da.

    Das wird schon, richtig aufgebaut ist "die Execute" auch kürzer, da bin ich mir sicher.

    Gehe meinen letzten Post einfach Schritt für schritt durch, das wird schon. Glaub mir.
    Es geht im Grunde nur um die ObservableCollection(Of usedNodes) statt der List(Of usedodes).

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Hallo Allerseits,

    also ich habe Anregung von @Nofear23m wie folgt umgesetzt:

    Im ViewModel:
    Spoiler anzeigen

    C#-Quellcode

    1. private ObservableCollection<Model.BusinesspartnerUsedNode> _listUsedNodes;
    2. public ObservableCollection<Model.BusinesspartnerUsedNode> ListUsedNodes
    3. {
    4. get
    5. {
    6. return _listUsedNodes;
    7. }
    8. set
    9. {
    10. _listUsedNodes = value;
    11. RaisePropertyChanged();
    12. }
    13. }
    14. private string _selectedNode;
    15. public string SelectedNode
    16. {
    17. get
    18. {
    19. return _selectedNode;
    20. }
    21. set
    22. {
    23. if (_selectedNode != value)
    24. {
    25. _selectedNode = value;
    26. RaisePropertyChanged();
    27. }
    28. }
    29. }


    Und im XAML:

    XML-Quellcode

    1. <ComboBox x:Name="cmbNode" HorizontalAlignment="Right" Margin="0,109,150,0" VerticalAlignment="Top" Width="220" ItemsSource="{Binding ListUsedNodes, Mode=TwoWay}" DisplayMemberPath="NodeName" SelectedValue="{Binding SelectedNode, Mode=TwoWay}"/>


    Ich musste jetzt den DisplayMemberPath setzen, woher soll die Combobox sonst wissen, was sie anzeigen soll.
    Es geht auch alles wieder wie bisher.

    Mein Problem aber kann ich nicht lösen, mir fehlen da zuviele Kenntnisse.
    Die ListUsedNodes kann bis zu 22 erlaubte Nodes (Knoten bzw. Datentabellen) als Auflistung enthalten. Jeder Knoten hat zwischen einer Handvoll bis zu über hundert mögliche Attribute (Datenfelder).
    In der hier besprochenen Comboboxen werden nur die verwendeten, also tatsächlich vorhandenen Knoten gezeigt und man kann dann einen beliebiegen Knoten hieraus auswählen.
    Nun möchte ich, sobald eine Auswahl getätigt wurde (konkret immer dann, wenn sich das angezeigte Item geändert hat), dass die 2. Combox entweder keine Daten hat (ist dann der Fall wenn kein Knoten vorhanden ist oder keiner gewählt ist) oder die tatsächlich in dem vorhandenen Knoten vorhandenen Attribute anzeigt.
    Das Problem ist, dass ich nicht weiß, wie ich das Befüllen bewerkstelligen soll.

    Im ViewModel habe ich
    Spoiler anzeigen

    C#-Quellcode

    1. private ObservableCollection<Model.UsedNodeAttribute> _listUsedAttributes;
    2. public ObservableCollection<Model.UsedNodeAttribute> ListUsedAttributes
    3. {
    4. get
    5. {
    6. return _listUsedAttributes;
    7. }
    8. set
    9. {
    10. _listUsedAttributes = value;
    11. RaisePropertyChanged();
    12. }
    13. }
    14. private string _selectedAttribute;
    15. public string SelectedAttribute
    16. {
    17. get
    18. {
    19. return _selectedAttribute;
    20. }
    21. set
    22. {
    23. if (_selectedAttribute != value)
    24. {
    25. _selectedAttribute = value;
    26. RaisePropertyChanged();
    27. }
    28. }
    29. }


    Im XAML habe ich eingetragen:

    XML-Quellcode

    1. <ComboBox x:Name="cmbAtt" HorizontalAlignment="Right" Margin="0,139,150,0" VerticalAlignment="Top" Width="220" ItemsSource="{Binding ListUsedAttributes, Mode=TwoWay}" DisplayMemberPath="AttName" SelectedValue="{Binding SelectedAttribute, Mode=TwoWay}"/>


    Wie ich die List ListUsedAttributes ist nicht meine Sorge, aber ich weiß nicht wie ich das anschubsen kann, ohne das SelectionChanged zu abonnieren.
    @Nofear23m: Was mir die ObservableCollection(Of UsedNodes) dabei helfen soll, habe ich nicht verstanden.

    Nachtrag: Ich habe einen Fehler noch beim Binden gemacht, im XAML musst ich nicht nur den DisplayMemberPath, sondern auch den SelectedValuePath setzen.
    Dann habe ich noch unter SelectedNode die ObservableCollection(Of usedNotdeAttribute) gesetzt und fertig.

    Gute Nacht allerseits :)
    Rob

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

    Dksksm schrieb:

    Was mir die ObservableCollection(Of UsedNodes) dabei helfen soll, habe ich nicht verstanden.

    Die ObservableCollection hilft dir insofern das sich die Listbox aktualisiert wenn Objekte in der Auflistung hinzukommen, entfernt werden oder aber verschoben werden.

    Dksksm schrieb:

    Ich habe einen Fehler noch beim Binden gemacht, im XAML musst ich nicht nur den DisplayMemberPath, sondern auch den SelectedValuePath setzen.
    Dann habe ich noch unter SelectedNode die ObservableCollection(Of usedNotdeAttribute) gesetzt und fertig.

    Das funktioniert, ist aber ein zu komplizierter Ansatz.

    Du hast ja dein Property SelectedNode. Dieses ist vom Typ UsedNodes. In UsedNodes gibt es das Property Attributes. Also musst du die Listbox nur auf dieses Binden.

    XML-Quellcode

    1. <ComboBox x:Name="cmbAtt" HorizontalAlignment="Right" Margin="0,139,150,0" VerticalAlignment="Top" Width="220" ItemsSource="{Binding SelectedNode.Attrributes, Mode=TwoWay}" DisplayMemberPath="AttName" SelectedValue="{Binding SelectedAttribute, Mode=TwoWay}"/>


    Somit kannst du dir das Property ListUsedAttributes sparen weil unnötig und einfacher.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Hallo @Nofear23m,

    ok, ich hatte in der Nacht nicht genügend getestet, mein Ansatz funktioniert nur "halb". Ich will da aber im Moment gerade nicht beigehen.
    Ich glaube ich beginne zu ahnen, was Du meinst. Von Collections etc. habe ich aber null Ahnung, ich muss mir das Wissen erst mal aneignen.
    Ich kenne nur streng typisierte Datasets aus Winworm. In diesem Projekt wollte ich mir nicht so viel arbeit machen und habe wahrscheinlich einen völlig kruden Ansatz gewählt.
    Wo siehst Du ein Property Attributes? Das gibt es eben nicht. Siehe meinen ersten Post.
    Nein ich war viel bekloppter, ich habe eine Liste in der ich alle erlaubten Node aufgelistet habe: ValideNodes
    Dann habe ich für jeden Knoten eine eigene List mit den Attributen.
    Im ersten (Prüf-)Durchlauf durch die XML fülle ich dann die Liste UsedNodes, ungültige Knoten werden entfernt und protokolliert.
    Das gleiche mit den Attributen in der XML, jedes verwendete Attribut wird in seine(!) Liste der UsedNodeXYAttributes geschrieben.

    Dann ist der Schritt fertig. Weil die XML extrem groß ist (mehrere 100 TSD Datensätze), lese ich sie Knoten für Knoten einzeln ein und schreibe sofort eine neue Ausgabe-XML.
    Im nächsten Schritt kann man dann aus der Liste der vorhandenen Knoten (UsedNodes) sich einen Knoten wählen. UsedNodes kennt selbst aber keine Attribute!
    Deshalb muss jetzt aus der Liste der verwendeten Attribute dieses Nodes erst einmal die ObservableCollection UsedAttributes aufgebaut werden.

    Das ist eben gar kein datensatztypischer Ansatz, ich weiß auch nicht wie man das macht, ehrlich gesagt. Ohne mein Dataset Designer bin ich da verloren.

    Gruß
    Rob

    Noch einen Satz: Ich habe grundsätzlich begriffen (beim Erklären kam mir so langsam die Erleuchtung) worauf Du hinaus willst und weiß, das mein Ansatz wirklich sehr krude ist.
    Das ganze auf eine vernunftige Collection umzustellen ist aber eine irre Fleißarbeit. Und ich müsste erst mal lernen, wie das überhaupt geht.
    Hallo

    OK, jetzt begreife ich langsam wie das bei dir ist. Die gute Nachricht.
    Dadurch das "wir" ja in der WPF Model und ViewModel normalerweise Trennen ist es uns egal wie die XML ist und/oder wie der Aufbau ist. Der Knackpunkt ist das ViewModel.
    In diesem bereiten wir die Daten auf wie wir sie haben wollen. Also so wie die View diese anzeigen soll.

    Das ist sogar simpler als du denkst. Man muss nur mal wissen wie, der Rest ist wirklich einfach.
    Kannst du eine Beispiel-App hochladen mit einem Beispiel XML? Ich kann dir dann gerne dein ViewModel "geraderücken" damit du siehst was ich meine. Erklären kan man das eher schlecht.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Hallo @Nofear23m,

    die Beispielanwendung habe ich versucht aufs nötigste abzuspecken. Eine kleine anonymisierte XML ist im Projekt eingebunden. Die "Lade-Funktion" öffnet auch keinen OpenFileDialog, sondern verwendet diese.
    Meine Anwenung funktioniert soweit, ist aber sicher nicht das Gelbe vom Ei.

    Ich würde schon gerne die Anwendung so umbauen, dass eine Klasse von UsedNodes auch die Attribute kennt. Ich weiß nur nicht, wie man das aufbaut. Denn ein Node darf ja nur einmal vorkommen, aber ein Node kann über 100 Attribute haben.
    Vielleicht kannst Du mir das ja mal zeigen. Ich meine sowas hier auch schon mal gesehen zu haben, nur finde ich das nicht .....

    Gruß
    Rob

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

    Hallo

    Ich habe gerade mal reingesehen. So ganz konnte ich nicht folgen, zumindest nicht auf die schnelle, aber so wie ich das sehe haben die Attribute jetzt nicht unbedingt direkt was mit UserNode zu tun und sich so im Model auch nicht miteinander "verbunden". Insofern ist die Methode welche du verwendest schon das richtige.

    Mein Ansatz wäre gewesen (wie es in MVVM wäre) das Model vom ViewModel zu trennen, da man hier dann gerade bei solchen Konstrukten mehr an überblick gewinnt. Das Model ist das eine, das ViewModel das andere.
    In solch einem Fall wären die Objekte im Model getrennt von einander, aber im VierwModel wären diese zusammen.

    Ich überlege gerade wegen einem MiniBeispiel welches ich erstellen können (In VB) welches dies veranschaulicht. Um dein C# Beispiel umzubauen würde ich zu lange brauchen da auch C# nicht so meins ist.
    Wäre es richtig das Produkte und Produktattribute im Grunde das wäre was du in deiner Anwendung auch machst. Also es gibt Produkte (Pullover) und die Produkte können Attribute haben. (Größe, Farbe, Stoff).
    Richtig?

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Hallo @Nofear23m

    Danke fürs reinsehen :)
    VB nach C# umschreiben ist für mich kein Problem, zur Not gibt es online Übersetzer, die mir helfen.
    Dein Produkte-Attribute Beispiel ist auch anwendbar. Oder Kontakte-Adressen, Telefonkontakt-Telefonnumer wäre auch nichts anderes.
    In alle Fällen gibt es Items, die Attribute haben, egal ob eines oder viele.

    Somit wäre so ein Minibeispiel für mich sehr Hilfreich. Mir geht es genau darum, wie ich das im Model beschreibe und im ViewModel zusammen setze. Das bring ich leider (noch) nicht von allein hin.

    Grüße
    Rob
    Hallo

    OK, ich werde heute Abend mal ein Beispiel mit Produkten und Produktattributen erstellen wo es viele Attribute gibt. Jedes Produkt kann dann x ttribute mit einem Wert haben.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Hallo @Dksksm

    So, ich habe mal ein Beispiel erstellt welches auf das mindeste Reduziert ist. Bitte nicht wundern wenn da ein paar Klassen drinnen sind die du noch nicht kennst (ServiceContainer, RelayCommand) oder Interfaces - die benötigt es einfach nur um MVVM einhalten zu können (ich kann hald nicht anders :( ).



    Aber im Grunde sollte der Code leicht verständlich sein, insbesonders der Aufbau von Model und ViewModel.

    Wenn du Fragen hast frag bitte nur.

    Grüße
    Sascha
    Dateien
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Hallo @Nofear23m,

    du bist ein Schatz :)
    Ich arbeite mich da erst mal in Ruhe durch. Danke!

    Grüße
    Rob

    Nachtrag: Naja, ich habe die RelayCommand Klasse bereits selbst in Benutzung, ist auch im Projekt von mir reichlich in Benutzung.

    Ich habe versucht dein Beispiel in mein Projekt zu implementieren, aber zumindest so wie dein Aufbau ist, kann ich es nicht verwenden.
    Dein Model Produkt habe ich mein Model Node gleichgesetzt.
    Bei Dir haben die Produkte aber immer die gleichen Nodes, bei mir sieht das ganz anders aus, denn jedes Node (entspricht einer DataTable) hat seine eigenen Attribute (Datenfelder).
    Damit kann ich also, wenn ich keinen Gedankenfehler habe, die Funktion ReloadVms() nicht nachbauen.

    Dein Beispielprojekt werde ich gut verwahren, denn für andere Projekte wird es mir eine gute Vorlage sein.
    Vielen Dank nochmals und eine gute Nacht :)
    Grüße
    Rob

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