Gibt es ein gutes formales Muster für die Verwaltung des Zustands in MVVM?

21

Ich habe angefangen, über Redux und React in der Web-Welt zu lernen, und je mehr ich darüber lerne, desto mehr wird mir klar, wie schmerzhaft die Statusverwaltung in der Desktop-Welt mit der MVVM-Architektur von WPF ist (mit Caliburn speziell zum Binden von Views) zu ViewModels).

Redux verfügt über einige einfache Prinzipien, die vorgeben, wie der Status verwaltet werden soll, sodass Benutzeroberflächenaktualisierungen, Ereignisbehandlung und Statusänderungen viel vorhersehbarer sind. Die Prinzipien sind:

  • Eine einzige Quelle der Wahrheit (alle veränderlichen Zustände sind in einem einzigen gemeinsamen Objekt gespeichert).
  • Der Status ist schreibgeschützt. Es kann nicht von Komponenten im gesamten Code geändert werden, was normalerweise in WPF der Fall ist.
  • Der Zustand kann nur durch reine Funktionen geändert werden.

Mit der MVVM-Architektur von WPF können Sie sehr schnell interaktive Ansichten erstellen. Das Debuggen von Problemen, wenn verschiedene Ansichtsmodelle und Ereignisse den Status ändern, ist jedoch ein Albtraum. Beispiel: Ein Ereignis wurde ausgelöst, bei dem eine Ansicht geändert und versucht wurde, eine Standardregisterkarte festzulegen, die Daten wurden jedoch nicht asynchron von einem Webdienst geladen, sodass die Registerkarte (noch) nicht vorhanden ist und nichts passiert

Ich habe Stunden damit verbracht, Diagramme zu zeichnen, um zu versuchen, komplexe Interaktionen zwischen miteinander verknüpften viewModels-Komponenten zu verstehen, die sich gegenseitig aktualisieren.

Ich verstehe, dass Redux versucht, einen Teil dieser unvorhersehbaren Zustände zu lösen. Gibt es etwas Ähnliches oder ein Architekturmuster, das gut zu WPF passt, um den Zustand besser zu verwalten? Ich bin nicht sicher, wie gut die Redux-Prinzipien in .NET funktionieren würden, da ich sie noch nicht ausprobiert habe. Vielleicht hat jemand Erfahrung, die einen Rat geben kann?

willem
quelle
Wir haben ähnliche Probleme im Browser. Direkte Javascript wird so früh ausgeführt und das DOM ist noch nicht gebaut, so dass man keine UI-Elemente finden kann. Glücklicherweise gibt es eine Reihe von Ereignissen, mit denen wir die verzögerte Ausführung einiger Skripte auslösen können, bis andere Dinge weiter fortgeschritten sind. (Wie DOMContentLoaded.)
Erik Eidt
1
Status in Redux wird tatsächlich aktualisiert, nie geändert.
Andy
1
Ich weiß, dass ich zu spät zur Party komme , aber es gibt ein Projekt namens React.NET , das die Redux-Architektur auf .NET bringt.
SiberianGuy
Für diejenigen, die den Ansatz von ngrx / store in Angular-Projekten mögen , gibt es NetRx.Store - State Management für .Net-Projekte, inspiriert von ngrx / store. Sie finden es auch bei Nuget . Es gibt auch ein gutes Beispiel für die Verwendung von NetRx.Store mit MVVM-Muster im WPF-Projekt
Vitalii Ilchenko

Antworten:

8

Ich glaube ich weiß was du meinst. Grundsätzlich lösen Sie das Problem, indem Sie entweder ein "Controller" - oder ein "Master" -Ansichtsmodell hinzufügen (entschuldigen Sie den Psudocode).

dh

public class MasterVM
{
    public ChildVM View1 {get;set;}
    public ChildVM View2 {get;set;}

    private Data data;
    public MasterVM()
    {
        View1.OnEvent += updateData;
    }

    private Action<int> updateData(int value)
    {
         View2.Value = value;
    }
}

Wenn Sie dies mit dem Mediator-Muster tun, stelle ich mir die Klasse als Controller vor. dh

public class Controller
{
    public Controller(MediatorService m)
    {
        m.Subscribe("valueupdated", updateData);
    }

    private Action<int> updateData(int value)
    {
         m.Publish("showvalue", value);
    }
}

public class View2
{
    public View2(MediatorService m)
    {
        m.Subscribe("showvalue", (int v)=> {Value = v;});
    }
}

Auf diese Weise können Sie Ihre 'Ablauflogik' oder Event Orchestration in diese hochgradig beständigen Klassen einordnen und den Code der VMs unbeschwert lassen. Wenn Sie ändern möchten, wenn der Benutzer auf KAUFEN klickt, wird die Bestellung verarbeitet. Informationen dazu finden Sie im 'OrderFlowController' oder 'OrderProcessVM' oder wie auch immer Sie sie benennen möchten. Anstelle einer Kombination aus BasketVM, PaymentVM, 3dSecureVM etc etc

In Ihrem konkreten Beispiel für die Registerkarte "Noch nicht bereit" haben Sie möglicherweise Folgendes

public class Controller
{
    bool dataLoadCompleted;
    public Controller(MediatorService m)
    {
        m.Subscribe("setTabRequest", setTab); //message from view model with set tab button
        m.Subscribe("dataLoadComplete", dataLoadComplete); //message from data loading view model or some other controller?
    }

    private Action<int> setTab(int value)
    {
         if(!dataLoadCompleted)
         {
             m.Publish("error", "Please wait for data to load"); //message for error alert view model
         }
         else
         {
             m.Publish("setDefaultTab", value); //message for tab viewmodel
         }
    }

    private Action dataLoadComplete()
    {
         //persist state;
         dataLoadCompleted = true;
    }
}
Ewan
quelle