Declarative Navigation using BuildIt.Lifecycle

Yesterday I showed in the post, Navigation using BuildIt.Lifecycle, that by wiring up to events in the HomeViewModel, the PrimaryRegion could then change the application state, thereby causing a navigation to the corresponding page. Since this pattern is likely to be so common I felt there was an opportunity to refactor it. In this post I’ll show a couple of ways that we can now re-write this code.

The first is just a refactoring of what we did yesterday. There are now OnEvent and ChangeState extension methods that allow for a declarative way to say that when an event occurs on the ViewModel, it should correspond to a change in state. You will notice that the method for declaring the state has changed from DefineViewModelState to StateWithViewModel, and that we now have an EndState method to conclude the state declaration. This is all in aid of trying to improve the fluid usage of this library and we’re likely to see a number of iterations of this before it settles, so apologies if this causes you to rework your code.

StateManager.GroupWithViewModels<AppStates>()
    .StateWithViewModel<AppStates, MainViewModel>(AppStates.Home)
        .OnEvent((vm, a) => vm.ShowSettings += a,
            (vm, a) => vm.ShowSettings -= a)
        .ChangeState(AppStates.Settings)
        .OnEvent((vm, a) => vm.ShowAbout += a,
            (vm, a) => vm.ShowAbout -= a)
        .ChangeState(AppStates.About)
        .EndState()
    .StateWithViewModel<AppStates, SettingsViewModel>(AppStates.Settings)
        .EndState()
    .StateWithViewModel<AppStates, AboutViewModel>(AppStates.About);

This refactoring was all very good except it still required events to be declared in the ViewModel, including invoking methods etc. I suspect that the most common use of this pattern will be for a single event to be declared which includes an enumeration indicating what type of “completion” the ViewModel. For example in this case we’d have an enumeration which includes Settings and About values, and depending on which value is included in the event, we’d choose to change to the corresponding state (in theory you could use the same enumeration that is used to define the states (ie AppStates) but I would avoid this as it could get confusing as there would be an implied link, rather than explicit declaration of intent).

Rather than every developer having to implement this pattern, we can simply change our MainViewModel to inherit from BaseViewModelWithCompletion and declaring an enumeration, MainCompletion:

public enum MainCompletion
{
    Base,
    Settings,
    About
}

public class MainViewModel:BaseViewModelWithCompletion<MainCompletion>
{
    public void DisplaySettings()
    {
        OnComplete(MainCompletion.Settings);
    }
    public void DisplayAbout()
    {
        OnComplete(MainCompletion.About);
    }
}

With this change in place, we can simplify our state declarations further to:

StateManager.GroupWithViewModels<AppStates>()
    .StateWithViewModel<AppStates, MainViewModel>(AppStates.Home)
        .OnComplete(MainCompletion.Settings).ChangeState(AppStates.Settings)
        .OnComplete(MainCompletion.About).ChangeState(AppStates.About)
        .EndState()
    .StateWithViewModel<AppStates, SettingsViewModel>(AppStates.Settings)
        .EndState()
    .StateWithViewModel<AppStates, AboutViewModel>(AppStates.About);

What’s really interesting now is that we’ve got a very declarative way to define what the navigation is within our application. We have events that trigger changes between states. This is very similar to what you’d expect if you were looking at a diagram of a finite state machine where you have states, and triggers that cause changes between states. The logical extension of this would be to have some kind of dsl that could be used to describe this in some form of XML, making it possible to build a designer around it!

Leave a comment