it-swarm.com.de

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

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 das Statusmanagement in der Desktop-Welt mit MVVM von WPF ist. Stilarchitektur (Caliburn speziell zum Binden von Ansichten an ViewModels verwenden).

Redux hat einige einfache Prinzipien, die vorschreiben, wie der Status verwaltet werden soll, wodurch UI-Updates, Ereignisbehandlung und Statusänderungen viel vorhersehbarer werden. Die Prinzipien sind:

  • Eine einzige Quelle der Wahrheit (alle veränderlichen Zustände werden in einem einzigen gemeinsamen Objekt gespeichert).
  • Der Status ist schreibgeschützt. Es kann nicht durch 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, das eine Ansicht geändert und versucht hat, 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 Wechselwirkungen zwischen miteinander verbundenen viewModels-Komponenten zu verstehen, die sich gegenseitig aktualisieren.

Ich verstehe, dass Redux darauf abzielt, einige 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 mir nicht sicher, wie gut die Redux-Prinzipien in .NET funktionieren würden, da ich sie noch nicht ausprobiert habe. Vielleicht hat jemand Erfahrung, die Ratschläge geben kann?

22
willem

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 (Entschuldigung 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 "Flusslogik" oder Ereignis-Orchestrierung in diese übergeordneten Klassen auf hoher Ebene einfügen und den VM-Code leicht halten. Wenn Sie ändern möchten, wenn der Benutzer auf KAUFEN klickt, wird die Bestellung verarbeitet, was Sie im OrderFlowController oder in der OrderProcessVM suchen oder wie auch immer Sie sie benennen möchten. Anstelle einer Kombination aus BasketVM, PaymentVM, 3dSecureVM usw. usw.

In Ihrem speziellen Beispiel für die Registerkarte "Noch nicht fertig" haben Sie möglicherweise

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;
    }
}
8
Ewan