21

I have started learning about Redux and React in the web-world, and the more I learn about it the more I'm realizing how painful state management is in the desktop-world with WPF's MVVM-style architecture (using Caliburn specifically to bind Views to ViewModels).

Redux has a few simple principles that dictate how state should be managed, making UI updates, event handling and state changes much more predictable. The principles are:

  • A single source of truth (all mutable state is stored in a single shared object).
  • State is read-only. It cannot be modified by components through-out the code, which is typically what happens in WPF.
  • State can only be modified by pure functions.

WPF's MVVM architecture allows you to build interactive views very quickly, but debugging issues when various viewmodels and events all change state is a nightmare. For example: an event fired that changed a view and tried to set a default tab, but the data has not finished loading asynchronously from a web service so the tab doesn't exist (yet) so nothing happens

I've spent hours drawing diagrams to try and understand complex interactions between inter-related viewModels components that update one-another.

I understand that Redux aims to solve some of this state unpredictability. Is there something similar, or an architectural pattern that would fit nicely with WPF to help manage state better? I'm not sure how well the Redux principles would work in .NET as I have not tried them yet. Perhaps somebody has some experience that can give some advice?

  • We have a similar kind of problems in the browser. Straightforward Javascript will run so early and the DOM isn't built yet, so one can't find any UI elements. Fortunately there are a number of Events, we can use to trigger delayed execution of some script until other things are further along. (Such as DOMContentLoaded.) – Erik Eidt Apr 27 '16 at 15:08
  • 1
    State in redux is actually updated, never modified. – Andy Apr 27 '16 at 17:26
  • 1
    I know i'm late to the party but there's a project called React.NET that brings Redux architecture to .NET. – SiberianGuy Jan 17 '17 at 17:26
  • For those who like the approach of ngrx/store in Angular projects, there is NetRx.Store - state management for .Net projects, inspired by ngrx/store. You can find it at Nuget as well. Also there is a good sample of usage NetRx.Store with MVVM pattern in WPF project – Vitalii Ilchenko Oct 9 '18 at 13:58
8

I think I know what you mean. Basically you solve the problem by adding either a 'controller' or a 'master' viewmodel (excuse psudocode)

ie

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

when you do this with the Mediator Pattern, I think of the class as a Controller. ie.

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

This kind of thing allows you to put your 'flow logic' or Event Orchestration in these high level persisted classes and keep the VMs code light. If you want to change 'when user clicks BUY, the order is processed' kind of things you know to look in the 'OrderFlowController' or 'OrderProcessVM' or however you want to name them. Rather than a combination of the BasketVM, PaymentVM, 3dSecureVM etc etc

So in your specific example of the 'tab not ready yet' you might have

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.