Есть ли хороший формальный шаблон для управления состоянием в MVVM?

21

Я начал изучать Redux и React в веб-мире, и чем больше я узнаю об этом, тем больше осознаю, насколько болезненным является управление состоянием в настольном мире с архитектурой в стиле MVF в WPF (специально использующей Caliburn для привязки Views). для ViewModels).

Redux имеет несколько простых принципов, которые определяют порядок управления состоянием, делая обновления пользовательского интерфейса, обработку событий и изменения состояния гораздо более предсказуемыми. Принципы:

  • Единственный источник правды (все изменяемые состояния хранятся в одном общем объекте).
  • Состояние только для чтения. Он не может быть изменен компонентами по всему коду, что обычно происходит в WPF.
  • Состояние может быть изменено только чистыми функциями.

Архитектура MVFM в WPF позволяет очень быстро создавать интерактивные представления, но отладка проблем, когда различные модели и события всех состояний изменяют состояние, является кошмаром. Например: произошло событие, которое изменило представление и попыталось установить вкладку по умолчанию, но данные не завершили асинхронную загрузку из веб-службы, поэтому вкладка не существует (пока), поэтому ничего не происходит

Я потратил часы на рисование диаграмм, чтобы попытаться понять сложные взаимодействия между взаимосвязанными компонентами viewModels, которые обновляют друг друга.

Я понимаю, что Redux стремится решить некоторые из этих состояний непредсказуемости. Есть ли что-то похожее или архитектурный шаблон, который бы прекрасно подходил для WPF, чтобы помочь лучше управлять состоянием? Я не уверен, насколько хорошо принципы Redux будут работать в .NET, так как я еще не пробовал их. Возможно, у кого-то есть опыт, который может дать совет?

Виллем
источник
У нас аналогичные проблемы в браузере. Простой Javascript будет запущен так рано, а DOM еще не создан, поэтому невозможно найти элементы пользовательского интерфейса. К счастью, существует ряд событий, которые мы можем использовать для запуска отложенного выполнения какого-либо сценария, пока другие вещи не будут продвинуты. (Например, DOMContentLoaded.)
Эрик Эйдт,
1
Состояние в редуксе фактически обновляется, а не изменяется.
Энди
1
Я знаю, что опаздываю на вечеринку, но есть проект под названием React.NET, который переносит архитектуру Redux в .NET.
SiberianGuy
Для тех, кому нравится подход ngrx / store в проектах Angular, есть NetRx.Store - управление состоянием для проектов .Net, вдохновленное ngrx / store. Вы также можете найти его на Nuget . Также есть хороший пример использования NetRx.Store с шаблоном MVVM в проекте WPF
Виталий Ильченко

Ответы:

8

Я думаю, я знаю, что вы имеете в виду. По сути, вы решаете проблему, добавляя либо «контроллер», либо «мастер» модель представления (извините, псевдокод)

т.е.

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

когда вы делаете это с Паттерном посредника, я думаю о классе как о контроллере. то есть.

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

Подобные вещи позволяют вам помещать вашу «логику потока» или «оркестровку событий» в эти постоянные классы высокого уровня и сохранять код ВМ легким. Если вы хотите изменить «когда пользователь нажимает кнопку« КУПИТЬ », заказ обрабатывается - то, что вы знаете, смотрите в« OrderFlowController »или« OrderProcessVM »или как бы вы ни назвали их. Вместо комбинации BasketVM, PaymentVM, 3dSecureVM и т. Д. И т. Д.

Так что в вашем конкретном примере «вкладка еще не готова» вы могли бы иметь

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
источник