Czy istnieje dobry wzór formalny do zarządzania stanem w MVVM?

21

Zacząłem uczyć się o Redux i React w świecie internetowym, a im więcej się o tym dowiaduję, tym bardziej zdaję sobie sprawę z tego, jak bolesne jest zarządzanie stanem w świecie komputerów z architekturą w stylu MVVM WPF (używając Caliburn specjalnie do łączenia widoków do ViewModels).

Redux ma kilka prostych zasad, które określają sposób zarządzania stanem, dzięki czemu aktualizacje interfejsu użytkownika, obsługa zdarzeń i zmiany stanu są znacznie bardziej przewidywalne. Zasady są następujące:

  • Jedno źródło prawdy (wszystkie zmienne stany są przechowywane w jednym wspólnym obiekcie).
  • Stan jest tylko do odczytu. Nie można go modyfikować przez składniki w całym kodzie, co zwykle dzieje się w WPF.
  • Stan można modyfikować tylko za pomocą czystych funkcji.

Architektura MVVM WPF pozwala bardzo szybko budować interaktywne widoki, ale problemy z debugowaniem, gdy różne modele i zdarzenia zmieniają stan, to koszmar. Na przykład: uruchomione zdarzenie, które zmieniło widok i próbowało ustawić domyślną kartę, ale dane nie zakończyły się ładowaniem asynchronicznym z usługi internetowej, więc karta nie istnieje (jeszcze), więc nic się nie dzieje

Spędziłem godziny rysując diagramy, aby zrozumieć złożone interakcje między wzajemnie powiązanymi komponentami viewModels, które się aktualizują.

Rozumiem, że Redux ma na celu rozwiązanie niektórych nieprzewidywalności tego stanu. Czy istnieje coś podobnego lub wzór architektoniczny, który pasowałby do WPF, aby lepiej zarządzać stanem? Nie jestem pewien, jak dobrze zasady Redux mogłyby działać w .NET, ponieważ jeszcze ich nie wypróbowałem. Być może ktoś ma jakieś doświadczenie, które może udzielić porady?

willem
źródło
Podobne problemy występują w przeglądarce. Prosty Javascript będzie działał tak wcześnie, a DOM nie został jeszcze zbudowany, więc nie można znaleźć żadnych elementów interfejsu użytkownika. Na szczęście istnieje wiele zdarzeń, których możemy użyć, aby wywołać opóźnione wykonanie niektórych skryptów, dopóki inne rzeczy nie pojawią się dalej. (Takich jak DOMContentLoaded.)
Erik Eidt 27.04.16
1
Stan w redux jest faktycznie aktualizowany, nigdy nie modyfikowany.
Andy
1
Wiem, że jestem spóźniony na imprezę, ale istnieje projekt o nazwie React.NET, który wprowadza architekturę Redux do .NET.
SiberianGuy
Dla tych, którzy lubią podejście ngrx / store w projektach Angular, jest NetRx.Store - zarządzanie stanem dla projektów .Net, inspirowane przez ngrx / store. Można go również znaleźć w Nuget . Jest też dobra próbka użycia NetRx.Store ze wzorem MVVM w projekcie WPF
Vitalii Ilchenko

Odpowiedzi:

8

Myślę, że wiem o co ci chodzi. Zasadniczo rozwiązujesz problem, dodając „kontroler” lub „master” viewmodel (usprawiedliwienie psudocode)

to znaczy

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

kiedy robisz to z Wzorem Pośrednika, myślę o klasie jako Kontrolerze. to znaczy.

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;});
    }
}

Tego rodzaju rzeczy pozwalają umieścić „logikę przepływu” lub koordynację zdarzeń w tych trwałych klasach wysokiego poziomu i utrzymywać lekki kod maszyn wirtualnych. Jeśli chcesz zmienić „, gdy użytkownik kliknie KUP, zamówienie jest przetwarzane” w rodzaju „OrderFlowController” lub „OrderProcessVM” lub jak chcesz je nazwać. Zamiast kombinacji BasketVM, PaymentVM, 3dSecureVM itp

Więc w konkretnym przykładzie „karta jeszcze niegotowa” możesz mieć

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
źródło