Getting Started with BuildIt.Lifecycle

Getting Started with BuildIt.Lifecycle

Towards the end of last year I started playing with the concept of using states to manage more than just visual states on a page. States are already used to manage the internal states of a control (eg Pressed state of a button). If you think about the pages that make up an application, they also represent, at some level, the states of an application. As I discussed in my post, States v’s Regions for Multi-Window Applications, there is also a need for regions to accommodate for platforms where there are multiple windows and/or secondary screens. In this post I’m going to show how to get started with an incubatory framework that uses states as the primary mechanism to determine navigation.

We’re going to start with a UWP application based on the Blank App (Universal Windows) new project template.

image

Next, we’ll add a Core library based on the Class Library (Portable for iOS, Android and Windows) new project template.

image

Before writing code, I’ll add a NuGet reference to BuildIt.Lifecycle to the both projects, and then an additional reference to BuildIt.Lifecycle.UWP package to the UWP project. I’ll also add a reference to the SimpleStates.Core PCL project to the SimpleStates UWP project.

To get started I’m just going to wire up three pages, Home, Settings and About. Note that the BuildIt.Lifecycle library uses the term View in place of Page, to be more consistent with the non-XAML platforms. For this reason I often interchange between them. More importantly though, we need to step back and put these pages into the context of states within the app. Once the app has launched it will transition to the Home state. From there the user can transition the app to either the Settings or the About state. The user must then return to the Home state before going to a different state. If you feel overly enthusiastic you could actually create a state diagram that represents this. I’ll represent this using an enum to define what the state values are.

public enum AppStates
{
    Base,
    Home,
    Settings,
    About
}

Each state is to be associated with both a Page and a corresponding ViewModel. To this end, I’ll create three pages, HomePage, SettingsPage and AboutPage in the UWP project, and I’ll create three ViewModels, HomeViewModel, SettingsViewModel and AboutViewModel in the PCL. The association between the AppStates enumeration values and their corresponding ViewModel will be done in a minute when I use an instance of the StateManager to define the states of the application. Some frameworks, like MvvmCross, rely on a convention to associate the page with the viewmodel (and implicitly the state of the application). To avoid reflection, this framework requires manual association between the page and the corresponding state. So I need to add the following calls to the beginning of the constructor in the App.xaml.cs file.

public App()
{
    LifecycleHelper.RegisterView<Pages.MainPage>().ForState(AppStates.Home);
    LifecycleHelper.RegisterView<SettingsPage>().ForState(AppStates.Settings);
    LifecycleHelper.RegisterView<AboutPage>().ForState(AppStates.About);
As I mentioned I need to create an instance of the StateManager which will track the states of the application, including what the current state is (ie what page is currently being displayed). In order to do this I need an entity that holds the instance of the StateManager, and will effectively manage the current state of the main window of the application.

public class PrimaryRegion : ApplicationRegion, IHasStates
{
    public IStateManager StateManager { get; }

    public PrimaryRegion()
    {
        var ssm = new StateManager();
        var sm = ssm.GroupWithViewModels<AppStates>().Item2;
        sm.DefineViewModelState<MainViewModel>(AppStates.Home);
        sm.DefineViewModelState<SettingsViewModel>(AppStates.Settings);
        sm.DefineViewModelState<AboutViewModel>(AppStates.About);
        StateManager = ssm;
    }
}

You’ll notice that I refer to this as a region. This is because this framework understands about application regions, such as multiple windows, and whilst it’s not a topic for this post, it allows you to be able to manage those windows. What we do need to do is create a region manager that knows about the PrimaryRegion class, and in fact will set that region to be the default region:

public class SimpleStatesApplication : RegionAwareBaseApplication<PrimaryRegion>
{
    protected override void DefineApplicationRegions()
    {
        // We don’t need to do anything as the PrimaryRegion automatically gets defined
        // use “RegionManager.DefineRegion<AnotherRegion>();” to register other regions
    }
}

The primary region needs to override the CompleteStartup task to force an initial transition to the Home state.

protected override async Task CompleteStartup()
{

    await base.CompleteStartup();

    await StateManager.GoToState(AppStates.Home, false);
}

This application can now be run and the Home page will be presented, and actually will have the corresponding MainViewModel set as its DataContext. In the next post I’ll discuss navigating between pages (ie transitioning to different states).

Connecting Universal Windows Platform Visual States to the StateManager

Connecting Universal Windows Platform Visual States to the StateManager

In my post State Management using BuildIt.States I made changes to elements on the page in a Universal Windows Platform (UWP) application using a StateManager. This was done to demonstrate the StateManager and this would work on any platform. However, for XAML platforms, this would normally be done via visual states. The following would describe the same states and corresponding changes to the UI.

<VisualStateManager.VisualStateGroups>
    <VisualStateGroup x_Name=”MainStatesGroup”>
        <VisualState x_Name=”StateOne”>
            <VisualState.Setters>
                <Setter Target=”HelloTextBlock.(TextBlock.Text)” Value=”State One” />
            </VisualState.Setters>
        </VisualState>
        <VisualState x_Name=”StateTwo”>
            <VisualState.Setters>
                <Setter Target=”HelloTextBlock.(TextBlock.Text)” Value=”State Two” />
            </VisualState.Setters>
        </VisualState>
        <VisualState x_Name=”StateThree”>
            <VisualState.Setters>
                <Setter Target=”HelloTextBlock.(TextBlock.Text)” Value=”State Three” />
            </VisualState.Setters>
        </VisualState>
        <VisualState x_Name=”StateFour”>
            <VisualState.Setters>
                <Setter Target=”HelloTextBlock.(TextBlock.Text)” Value=”State Four” />
            </VisualState.Setters>
        </VisualState>
    </VisualStateGroup>
</VisualStateManager.VisualStateGroups>

Connecting the ViewModel StateManager (which I setup in the post BuildIt.States from a ViewModel) to the visual states can be done using the VisualStateChanger class that exists in the BuildIt.Lifecycle.UWP library (https://www.nuget.org/packages/BuildIt.Lifecycle.UWP/). Again, this code is a little unrefined – I’ll be making changes to make it easier to connect up the VisualStateChanger without having to retrieve the StageGroup instance.

private VisualStateChanger<MainStates> Changer { get; set; }
protected override void OnNavigatedTo(NavigationEventArgs e)
{
    base.OnNavigatedTo(e);

    var sm = ViewModel.StateManager;
    var grp = sm.StateGroups[typeof(MainStates)] as StateGroup<MainStates, DefaultTransition>;
    Changer = new VisualStateChanger<MainStates>(this, grp);
}

Refactoring StateManager to StateManager Binding

Refactoring StateManager to StateManager Binding

In my previous post, BuildIt.States from a ViewModel, I showed a rather messy way to connect on StateManager to another. I’m just in the process of refactoring BuildIt.States to include an extra method on the IStateManager interface to enable simple binding between two StateManagers, and their respective StateGroups. This would significantly simplify the code necessary, as well as ensuring all StateGroups within the two StateManagers are synchronized.

private IStateBinder Binder { get; set; }
protected override void OnNavigatedTo(NavigationEventArgs e)
{
    base.OnNavigatedTo(e);

    Binder = manager.Bind(ViewModel.StateManager);
}
protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
    base.OnNavigatingFrom(e);

    Binder?.Unbind();
    Binder = null;
}
I’ll be updating the BuildIt.States NuGet package in the coming days to include this additional method.

BuildIt.States from a ViewModel

BuildIt.States from a ViewModel

In my previous post on State Management using BuildIt.States I demonstrated using the BuildIt.States library to declare states for a page. However, it’s not enough to declare states in the UI space. The management of state should be done in the ViewModel space, making it possible to test and keep the logic separate from the UI implementation.

To do this, I can create another instance of the StateManager within the ViewModel for the page. I need to declare the same states, just without any of the additional attributes required to update the user interface. The ViewModel ends up looking like the following – I’ve included a StateName property and a method, UpdateState, which can be invoked to change the state.

public class MainViewModel : NotifyBase, IHasStates
{
    public IStateManager StateManager { get; } = new StateManager();
    private string stateName;
    public string StateName
    {
        get { return stateName; }
        set
        {
            stateName = value;
            OnPropertyChanged();
        }
    }
    public MainViewModel()
    {
        StateManager.Group<MainStates>()
            .DefineState(MainStates.StateOne)
            .DefineState(MainStates.StateTwo)
            .DefineState(MainStates.StateThree)
            .DefineState(MainStates.StateFour);
    }
    public async Task UpdatState(MainStates state)
    {
        var ok = await StateManager.GoToState(state);
        if (ok)
        {
            StateName = state.ToString();
        }
    }
}

Now, in the UI space, the page needs to wire to and unwire from the StateChanged event handler that is available on each of the StateGroup entities in the StateManager.

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    base.OnNavigatedTo(e);

    var sm = ViewModel.StateManager;
    var grp = sm.StateGroups[typeof(MainStates)] as StateGroup<MainStates, DefaultTransition>;
    grp.StateChanged += Grp_StateChanged;
}
protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
    base.OnNavigatingFrom(e);

    var sm = ViewModel.StateManager;
    var grp = sm.StateGroups[typeof(MainStates)] as StateGroup<MainStates, DefaultTransition>;
    grp.StateChanged -= Grp_StateChanged;
}

private void Grp_StateChanged(object sender, StateEventArgs<MainStates> e)
{
    manager.GoToState(e.State, e.UseTransitions);
}

I also updated each of the event handlers for the buttons to call the UpdateState method on the ViewModel. Now I can control the state of the UI by updating the state of the StateManager within the ViewModel.

State Management using BuildIt.States

State Management using BuildIt.States

Over the last month or so I’ve been refactoring some code I prototyped earlier in the year that allows for easier state management within an entity. One of the outputs of this was a portable library that’s available via NuGet, and the source is available via the BuildIt repository on GitHub (https://github.com/builttoroam/BuildIt). In my post, Cross Platform Visual States, I gave an example of how to define states for a Xamarin.Forms page. The StateManager can be used in applications for any supported platform, making it easy to define states. In this post, I’ll provide a quick walk through of using the StateManager, this time in a UWP project as an alternative to using visual states.

To get started, add a reference to the BuildIt.States NuGet plackage. Then in the codebehind file for the page, create an instance of the StateManager class.

private readonly IStateManager manager = new StateManager();

Next, we’re going to create an enumeration that lists each of the possible states. Note that we include a placeholder “Base” state which will represent the default state before any call to change state. If you don’t define a placeholder state, the first state will become the default, meaning that any initial transition to the first state won’t have any effect, since the StateManager thinks it’s already in that state.

private enum MainStates
{
    Base,
    StateOne,
    StateTwo,
    StateThree,
    StateFour
}

I’ll add some XAML to the page in order to show a label using a TextBlock and some buttons that will trigger the state changes.

<Page x_Class=”StateManagerIntro.MainPage”
      http://schemas.microsoft.com/winfx/2006/xaml/presentation"”>http://schemas.microsoft.com/winfx/2006/xaml/presentation”
      http://schemas.microsoft.com/winfx/2006/xaml"”>http://schemas.microsoft.com/winfx/2006/xaml”
      http://schemas.microsoft.com/expression/blend/2008"”>http://schemas.microsoft.com/expression/blend/2008″
      http://schemas.openxmlformats.org/markup-compatibility/2006"”>http://schemas.openxmlformats.org/markup-compatibility/2006″
      mc_Ignorable=”d”>

    <StackPanel VerticalAlignment=”Center”
                HorizontalAlignment=”Center”>
        <TextBlock Text=”Hello World!”
                   x_Name=”HelloTextBlock” />
        <Button Click=”FirstStateClick”>State One</Button>
        <Button Click=”SecondStateClick”>State Second</Button>
        <Button Click=”ThirdStateClick”>State Third</Button>
        <Button Click=”FourthStateClick”>State Fourth</Button>
    </StackPanel>
</Page>

private void FirstStateClick(object sender, RoutedEventArgs e)
{
    manager.GoToState(MainStates.StateOne);
}

private void SecondStateClick(object sender, RoutedEventArgs e)
{
    manager.GoToState(MainStates.StateTwo);
}

private void ThirdStateClick(object sender, RoutedEventArgs e)
{
    manager.GoToState(MainStates.StateThree);
}

private void FourthStateClick(object sender, RoutedEventArgs e)
{
    manager.GoToState(MainStates.StateFour);
}

The event handlers for the buttons invoke GoToState on the StateManager in order to invoke a state change to the specified enum value. The only thing left to do is to define the different states.

manager.Group<MainStates>()
    .DefineState(MainStates.StateOne)
        .Target(HelloTextBlock)
        .Change(x=>x.Text,(x,v)=>x.Text=v)
        .ToValue(“State One”)

    .DefineState(MainStates.StateTwo)
        .Target(HelloTextBlock)
        .Change(x => x.Text, (x, v) => x.Text = v)
        .ToValue(“State Two”)

    .DefineState(MainStates.StateThree)
        .Target(HelloTextBlock)
        .Change(x => x.Text, (x, v) => x.Text = v)
        .ToValue(“State Three”)

    .DefineState(MainStates.StateFour)
        .Target(HelloTextBlock)
        .Change(x => x.Text, (x, v) => x.Text = v)
        .ToValue(“State Four”);

The fluid nature of the state declarations make it relatively straightforward to see what each state defines. In each case, the state changes the Text on the HelloTextBlock to a value that matches the state. When this application is run, preseing each of the buttons invokes the state change, updating the Text on the HelloTextBlock accordingly.

States v’s Regions for Multi-Window Applications

States v’s Regions for Multi-Window Applications

In previous posts I’ve talked a lot about visual states, application state and control states. However, one thing that I’ve been trying to wrap my head around is the disconnect that happens when you start to consider that an application can have multiple windows.What’s rather ironic is multi-window support was something we took for granted back when we were building complex line of business Windows Forms/WPF applications. However, the iOS era lead to the dumbing-down of application development – in the Apple world users are only able to do single operations and apparently can’t wrap their heads around anything more than a single screen full of data at a time. This lead to application models, and page/view lifecycles that assume that the application is either in the foreground, or in the background; no middle ground where the application is visible, just not in focus, which is the case in a multi-windowed environment.

The interesting side effect of a multi-windowed environment is less to do with having multiple applications open and visible at the same time but that a single application can have multiple windows open at the same time. One of the best features of the old Live Messenger application was that you could have each chat open in a separate window, each of which could be independently moved, pinned and closed. Currently, nearly every messaging application, be it Messenger (Facebook), WhatsApp, Skype or Line, use a similar single-window model that’s extremely painful when you are actively participating in multiple chats.

Of course, most platforms all support some limited form of multiple window support, for example when projecting the contents of the screen to an external monitor. The Universal Windows Platform has full support for the creation and management of multiple windows within a single application instance. A recent post on the Redmond Pie shows a concept of what multiple windows on an iPad would look like. It’s evident that multi-windowed applications are going to play an important part of all future platforms.

This leads me to my next challenge. In the context of a single window application we can think of the current page of the application as mapping to a state in the application. Transition from one state to another equates to moving between pages. Within the confines of a page, there may be multiple states; these map to visual states on the page. However, going the other direction, how do we represent multiple windows? Windows don’t map to another higher level set of states, since there may be any number of windows opened, and they can all exist in relative independence.

So, we need a different concept to represent them, which I’ve termed Application Regions. By default all applications have a default or start up region. A region has a current states, which will normally correspond to the current page or view. Regions can be created and closed independently, and usually correspond to separate windows (although I’m currently considering whether a region can manifest itself in other ways). Regions can also have different startup pages (ie a different startup state), and might in fact have a completely different set of states.

In the same way that we need a state manager to assist with the management and tracking of states, we’ll need a region manager that understands how to create, track and close regions. Whilst the region manager will itself live in the platform agnostic core of our application, it will of course need a mechanism to spawn new windows (ie when a region is created). The region manager will also need to be clever enough to do the right thing when run on a platform that doesn’t support multiple windows. On such an environment, when a new region is spawned, the new set of pages/view will simply have to be added to the navigation stack of the existing window; when the user closes the region, the pages/views will be popped off the stack, returning to the last page/view of the previous region.

This is just some early stage thoughts on the concept of application regions v’s application states.