MVVM: Wie teilt man dem MainViewModel die UserControl-Auswahl mit?

  • VB.NET
  • .NET 7–8

Es gibt 24 Antworten in diesem Thema. Der letzte Beitrag () ist von Amro.

    MVVM: Wie teilt man dem MainViewModel die UserControl-Auswahl mit?

    Hallo zusammen.

    Ich bastel gerade eines meiner klassischen, privaten WinForms-Projekte zu einem WinForms-MVVM-Projekt um.
    Dafür habe ich mir ein spezielles Panel (ich hab's mal vorerst DataListPanel genannt) gemacht, welches ähnlich wie ein WPF-StackPanel UserControls (UCs) von oben nach unten stapelt, aber zusätzlich beim Mausbewegen Feedback gibt und beim UC-Anklicken dieses markiert, damit man weiß, was gerade selektiert ist.


    Nun das eigentliche MVVM-Problem: Da ich auf dem MainForm Buttons habe, die Aktionen mit dem selektierten Item machen sollen (also nicht mit dem selektierten UC, sondern mit dem dahinterliegenden Item, welches durch ein entsprechendes ItemViewModel als DataContext für das jeweilige UC hinterlegt ist), weiß ich grad nicht, wie ich dem MainViewModel, welches sich um die MainForm-Aufgaben kümmert, mitteilen darf, welches UC/ItemViewModel/Item selektiert ist. Den nicht MVVM-Weg habe ich: Ich caste aus dem DataContext des DataListPanels (welches ja Teil des MainForms ist und somit dessen DataContext bekommt) das MainViewModel und sage dem MainViewModel: selektiertes Item = DataContext des selektierten UC. Damit muss ich aber dem View Details über die ViewModels mitteilen :(

    kurz zusammengefasst:
    • habe eine Liste von UCs, bei der man ein UC auswählen kann
    • für das gewählte Item sollen Aktionen zugänglich sein, die aber in Form von (ToolStrip)Buttons auf dem MainForm liegen
    • Wie teile ich dem MainForm-ViewModel MVVM-konform mit, welches Item selektiert wurde?
    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“ ()

    Eigentlich musst du doch nur die SelectedItem-Property binden und in dem Setter kannst du dann noch weitere Parameter setzen.
    Musst dann eigentlich nur zusehen, dass du TwoWay setzt
    Quellcode lizensiert unter CC by SA 2.0 (Creative Commons Share-Alike)

    Meine Firma: Procyon Systems
    Meine Privatwebseite: SimonC.eu

    Bitte nicht wundern, wenn meine Aktivitäten im Forum etwas langsamer sind, ich baue gerade mein Nebengewerbe zum Vollgewerbe aus.
    Ich versuche auf euch zurückzukommen :)
    Hoffe ich hab dich richtig verstanden, wenn ja dann:

    Das Stichwort wäre eine SelctedItemProperty für das Item im ViewModel.
    Schau dir die Listbox in WPF an. Diese bietet in XAML eine Property SelectedItem an ,die
    an das ViewModel gebunden werden kann (an eine Property SelectedItem im ViewModel).
    Sehr einfach zu implementieren in WPF und trägt zum Verständnis für dein Problem bei.
    Danach kannst du versuchen das in WindowsForms umzusetzten.

    Ist dein DataListPanel an eine Liste im ViewModel gebunden?

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

    Ich glaub ich seh den Baum vor lauter Wäldern nicht.
    Also gut: ne SelectedIndex-Eigenschaft hab ich noch nicht, würde aber zum Ziel führen.
    Ne SelectedItem-Eigenschaft würde mich nur zu einem UserControl führen, da mein DataListPanel ein pures Control ist und gar nichts über ViewModels wissen sollte. Es kann mitteilen, welches UserControl selektiert ist. Aber da das UC im ViewModel nix zu suchen hat, weiß ich grad nicht, wie ich aus dem UC und dessen DataContext das ItemViewModel herholen darf.
    Das DataListPanel ist derzeit noch nicht an irgendwas gebunden, noch wird es manuell mit Daten gefüttert.
    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.

    VaporiZed schrieb:

    Also gut: ne SelectedIndex-Eigenschaft hab ich noch nicht, würde aber zum Ziel führen.

    VaporiZed schrieb:

    Ne SelectedItem-Eigenschaft würde mich nur zu einem UserControl führen

    Eine Kombination aus SelectedIndex und SelectedItem wäre glaube ich zielführend.
    Hab zuerst völlig überlesen, dass es eigenes Control ist.

    Müsste man jetzt nur noch schauen, wie man an die Daten kommt. Du könntest natürlich ein Interface einsetzen, welches eine Methode bietet, dass du die Daten zu einem bekannten Format wandelt. ​myItem.ToMyModel() und ggf. auf Fehler reagieren.
    Quellcode lizensiert unter CC by SA 2.0 (Creative Commons Share-Alike)

    Meine Firma: Procyon Systems
    Meine Privatwebseite: SimonC.eu

    Bitte nicht wundern, wenn meine Aktivitäten im Forum etwas langsamer sind, ich baue gerade mein Nebengewerbe zum Vollgewerbe aus.
    Ich versuche auf euch zurückzukommen :)
    Edit: Jetzt erst gesehen, dass es sich um eine WinForms-Anwendung handelt.
    In WinForms setze ich MVVM mithilfe eines globalen typDatasets um - dassis quasi mein MainViewmodel.
    Welches ich bei Bedarf mit PartialClasses erweitere, und auch die typTables und typDataRows erfahren partiale Erweiterungen.

    VaporiZed schrieb:

    Wie teile ich dem MainForm-ViewModel MVVM-konform mit, welches Item selektiert wurde?
    Das Winforms-BindungsSystem ist da ziemlich unzureichend, also muss man CodeBehind schreiben, und im BindingSource.CurrentChanged dann geeignete Methoden des erweiterten typDatasets aufrufen.

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

    Kannst du mir ein paar Informationen über dein DataListPanel geben.
    Ich würde das gerne nachbauen.
    Ich vermute mal das du einfach eine Listbox in einem UserControl hast.
    Das hat bestimmt paar neue Eigenschaften und irgenwelche Events.
    Wenn das so ist versuch deinem DataListPanel eine Eigenschaft zu geben die
    SelectedItem heist. Dann nimmst du das Event der ListBox , SelctedChange oder so und
    Weist der Eigenschaft die selectedItem der ListBox zu.
    Diese (neue Eigenschaft) kannst du dann Später an die ViewModel binden.
    (vielleicht INotifyPropertyChanged in DataListPanel implementieren).
    Also so würde ich es versuchen...
    Wir reden von Winforms oder?

    Nehme alles zurück. Du versucht gerade ein UserControl zu bauen das hat nix mit MVVM zu tun oder ViewModel. Das ist rein View (GUI). Da musst du anders rangehen. DeinUserControl musst du deiner BindingSource anpassen weil soweit ich mitbekommen hab ist Bindingsource wohl das Datacontext in WPF.
    Da es ein WindowsForms Control ist konsumiert es ja das ViewModel über die BindingSource. Ein DataContext das nach unten weitergereicht wird wie in WPF ist die BindingSource glaub ich nicht...


    Dieser Beitrag wurde bereits 9 mal editiert, zuletzt von „Amro“ ()

    Dein DatalistPanel wird ziemlich aufwändig werden.
    Dynamisch Controls anlegen, beschicken, auslesen oder databinden, und nach Datenlage auch wieder verschwinden lassen - "Pain in the Ass" sagt mein Chefe zu sowas.

    Und auf deim Bild sehe ich nix, was ein kunstvoll gestaltetes Datagridview nicht auch leisten könnte - Stichwort CellFormatting-Event und CellPainting-Event.

    ErfinderDesRades schrieb:

    "Pain in the Ass"


    Sympatisch :D

    Ist auch einer meiner Lieblingssprüche.
    Quellcode lizensiert unter CC by SA 2.0 (Creative Commons Share-Alike)

    Meine Firma: Procyon Systems
    Meine Privatwebseite: SimonC.eu

    Bitte nicht wundern, wenn meine Aktivitäten im Forum etwas langsamer sind, ich baue gerade mein Nebengewerbe zum Vollgewerbe aus.
    Ich versuche auf euch zurückzukommen :)

    Amro schrieb:

    ist Bindingsource wohl das Datacontext in WPF.
    Da es ein WindowsForms Control ist konsumiert es ja das ViewModel über die BindingSource. Ein DataContext das nach unten weitergereicht wird wie in WPF ist die BindingSource glaub ich nicht...
    Doch, seit .NET 7 gibt es einen weitergereichten DataContext. Aber ja, damit muss auch was gemacht werden, mämlich Zuordnung zu einer BindingSource, wenn man ihn nutzen will.

    Amro schrieb:

    das hat nix mit MVVM zu tun oder ViewModel
    Doch, schon. Ich will ja MVVM-konform das SelectedItem des DataListPanels dem MainForm(ViewModel) mitteilen.

    ErfinderDesRades schrieb:

    Und auf deim Bild sehe ich nix, was ein kunstvoll gestaltetes Datagridview nicht auch leisten könnte
    Da bin ich jetzt aber überrascht, dass Du keine relevanten Unterschiede siehst. Preselection (also Mausfeedback, über welchem Item man sich befindet) gibt's m.E. beim DGV nicht, eine CommandProperty bei ner DgvButtonCell auch (noch) nicht. Und das hier bekommt ein DGV definitiv nicht ohne Komplikationen hin, Stichwort Anzeige von komplexen UserControls:




    Habe es jetzt so hinbekommen, dass mein DataListPanel eine SelectedIndex-Eigenschaft hat, welche ans MainViewModel gebunden wird. Vielen Dank für die Idee @ErfinderDesRades. Dort wird dann über den SelectedIndex das SelectedItem festgelegt, welches dann durch die MainForm-Commands erreichbar ist. Will heißen: Das MainViewModel besitzt ja eine Auflistung aller ItemViewModels und dann gilt eben SelectedItem = ItemViewModels(SelectedIndex)
    Außerdem hat sich das nun mit dem manuellen Datenfüttern erledigt. Durch Setzen eines generischen Parameters vom Typ UserControl weiß das DLP nun, was es für "Zeilen" generieren soll. Und durch Setzen einer weiteren DesignerProperty kann ich auch noch die DataSource des DLP festlegen: die ItemViewModels-Sammlung im MainViewModel.
    Jetzt "nur noch" durch vermeintlich undokumentierte Tricks rausfinden, wie ich umgehen kann, dass dafür automatisch eine weitere BindingSource erstellt wird :/
    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.
    Ich weiß nicht genau ob du jetzt ein StackPanel nachbauen willst aber meine Bedenken sind diese:
    Ist das nicht so das du wieder dein ViewModel an dein DLP (View) anpasst.
    Kann das ViewModel in WPF dann einfach an ein anderes Control übergeben werden (bsp ListBox mit Template).
    Du baust ja ein DLP für deine Ansicht / Darstellung in ertse Linie und nicht für MVVM.
    Wenn MVVM Konform dann nur in sofern das dein DLP in der Lage ist ein ViewModel zu Konsumieren , sprich dessen Properties, und mehr erstmal nicht.
    So musst du doch in jedem anderen UI wieder ein Steuerelemnt bauen das sich so verhält wie dein DLP.
    Ich glaub du tappst da wieder in die Falle aus alter Gewohnheit der WindowsForms herangehensweise.
    Normal ist das Selected Item eigentlich für Daten und in Verbindung mit einer Liste/Observablecollection (ViewModel sollte nur Daten beinhalten). Du hast zum Beispiel eine List(of Person) mit Name und Alter.
    Diese Liste übergibst du als ItemSource und erstellst eine ItemTemplate für die ListBox. Das ListBoxItem bindet sein Template dann an Person.Name und Person.Alter. Ich denke im Hintergrund ist das nur eine Schleife die das List(of Person ) durchläuft und darstellt. In dein ViewModel machst du ein SelectedPerson und bindest es an SelctedItem der ListBox. Die Listbox kümmert sich um den rest.
    In deinen Fall hast du aber eine UserControl und keine Daten. Somit machst du das ViewModel wieder abhängig von der UI. Ist schon wieder zu eng gekoppelt. Dann musst du wieder abstrahieren mit Interfaces wie mit den IDialog um das für andere UI's nutzen zu können.
    Aber ich könnte auch falsch liegen und du hast das alles schon bedacht :)

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

    Amro schrieb:

    ob du jetzt ein StackPanel nachbauen willst
    Ja, mit erweiterten Eigenschaften (Vorselektion, Selektion).
    Ich verstehe Deine Bedenken. Einen SelectedIndex einzuarbeiten ist zwar vermeintlich einfach, macht das VM aber abhängig von der Implementierung. Ich sollte es also hinbekommen, ein SelectedItem zu erschaffen, welches dann zwischen DLP und VM per Binding mitgeteilt werden kann, sodass das VM vom DLP erfährt, was selektiert ist, das DLP aber keine Ahnung hat, was das nun selektierte Ding ist.
    Mal sehen, ob ich diese Sache lösen kann.

    ##########

    Argh, ich bin doch ein Vollpfosten. Ich hab doch quasi schon alles. Das SelectedItem des DLP ist der DataContext des selektierten UCs. Das kann ich jetzt an die MainVM-Property SelectedItem binden und dann den SelectedIndex wegwerfen.
    Hab's ausprobiert, klappt. :D
    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“ ()

    Sehr gut :thumbsup: .
    Dann machen wir mal weiter.
    Warum muss dein Viewmodel wissen welches UC selectiert wird.
    Vielleicht bekommen wir das besser hin.
    Ich glaub wenn deine DLP ein eignes DataContext/ViewModel hätte
    könntest du das besser lösen.
    Dann ist es rein View nicht mehr abhängig von anderen ViewModels.
    DLP ViewModel tauscht dann nur Properties mit den anderen Viewmodel aus.
    Könntest sogar ein StuerElementenBibliothek anlegen und in diese hat jedes Steuerelemt sein eignes ViewModel das nach aussen Komuniziert.
    Also warum muss MainViewModel wissen welches UC' selectiert wurde?

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

    Öh … weil das hier momentan mein GUI ist, welches ich auch so beibehalten will.

    in Gelb eine App-Versionsstatusleiste (eigenes Control, nicht weiter relevant)
    Darunter eine ToolStripleiste mit Befehlen für das selektierte Item: Neuanlage, zwei weitere Aktionen, ein ausgegrauter Wiederherstellbutton, falls man Änderungen bei einem Item vorgenommen hat, die man rückgängig machen will.
    Dann eine Kombi aus einer Titelleiste und dem dem DataListPanel, damit es Ähnlichkeit mit einem DGV hat. Wenn man runterscrollt (smooth, bei nem DGV ist es ja normalerweise zeilenweise), bleibt die Titlelleiste weiterhin sichtbar.
    Das DLP will ich aber auch in anderen Apps nutzen. Ich bin nur ab überlegen, ob mit Titelleiste oder ohne.
    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.
    Wie wäre es mit einem Model für das SelecedteItem (DLPItem).
    Dann kennt das ViewModel nur das Model für das DLPItem(Typ).
    Die Items sehen in deinem DLP ja immer gleich aus.
    Du könntest dann in jedem UI einfach dein Model binden an das Template/UserControl.


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

    Ich kann Dir als Classic-WinForms-Hobbyprogrammierer nicht folgen. Was meinst Du? Dass das MainVM nicht alle Items kennt, sondern immer nur das selektierte? Falls ja, seh ich grad die Umsetzung nicht in meinem Kopf. Falls Du noch mehr meinst, weiß ich nicht, was.
    Ich hatte das vor einigen Jahren mal mit @Nofear23m das erste Mal durchexerziert und er ist dann zu der Variante gekommen: Das MainVM kennt alle ItemVMs. Aber ich bin offen für Neues.
    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.
    Die VMs kennen sich alle untereinander das stimmt.
    Nicht nur das , im Idealfall haben die auch den gleichen
    Vorfahren.
    Wenn du nur das Vm des Selctierten Items wissen willst dann übergib doch einfach nur das VM.
    Wäre ja dann von Typ ViewModlBase.
    Das wäre ja dann richtig (MVVM). Und du kannst in MainVm mit dem ItemVm arbeiten.
    Das Item wird ja dann über die Bindung benachrichtig.

    Gab es ein Beispielprojekt?

    VaporiZed schrieb:

    Ich hatte das vor einigen Jahren mal mit @Nofear23m das erste


    ich kratze auch nur an der Oberfläche ;)

    VaporiZed schrieb:

    Hobbyprogrammierer



    Hier mal das aktuelle Testprojekt. Hat etwas gedauert, die Funktionalität zu stutzen und die Abhängigkeiten meiner Datenquellen und StandardDLLs zu beseitigen.
    Die Aktualisierung des DLPs nach Löschen oder Hinzufügen von Items ist noch getrickst, da das ItemVM das MainVM informiert, dieses die Löschung vornimmt und dann per ans DLP gebundene Property das DLP informiert, sodass sich das DLP um seine Items/UCs kümmert. In der WPF ginge sowas vielleicht automatisch per ObservableCollection, aber die hab ich ja noch nicht in WinForms. die scheint nur im DLP anzukommen, wenn das DLP selbst eine Property vom Typ ObservableCollection(Of ItemViewModel) hat. Eine angebundene ObservableCollection(Of Object) reagiert nicht. Dann kennt das DLP ja doch wieder was von den ViewModels.

    ##########

    Allerdings merk ich grad, dass ich im MainVM gar keine ObservableCollection(Of ItemViewModel) brauche, sondern dass eine ObservableCollection(Of Object) reicht. So kann ich im DLP auf die Events der ObservableCollection reagieren und das DLP entsprechend modifizieren.
    Dateien
    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 3 mal editiert, zuletzt von „VaporiZed“ ()