Nick's .NET Travels

Continually looking for the yellow brick road so I can catch me a wizard....

Toggling the SplitView Pane using a Hamburger Button in a UWP Application

Yesterday (Using the Universal Windows Platform SplitView Control with Visual States and BuildIt.Lifecycle) I introduced a SplitView into my sample application where I used a couple of buttons in the content. In a lot of applications the SplitView will be triggered by either AdaptiveTriggers used in conjunection with the visual states, or via a hamburger button. Today I’ll add both of these to our application. Firstly a couple of AdaptiveTriggers added to the visual state definitions:

<VisualStateManager.VisualStateGroups>
    <VisualStateGroup x:Name="SplitViewStates">
        <VisualState x:Name="Minimised">
            <VisualState.StateTriggers>
                <AdaptiveTrigger MinWindowWidth="0" />
            </VisualState.StateTriggers>
        </VisualState>
        <VisualState x:Name="Expanded">
            <VisualState.Setters>
                <Setter Target="RootSplitView.(SplitView.IsPaneOpen)"
                        Value="True" />
                <Setter Target="RootSplitView.(SplitView.DisplayMode)"
                        Value="Inline" />
            </VisualState.Setters>
            <VisualState.StateTriggers>
                <AdaptiveTrigger MinWindowHeight="0" MinWindowWidth="600" />
            </VisualState.StateTriggers>
        </VisualState>
    </VisualStateGroup>
</VisualStateManager.VisualStateGroups>
As the user adjusts the width of the window the Pane of the SplitView will open and close depending on whether the window is wide enough. Secondly, I need to add a hamburger button to the layout of the page. This is a regualr button control, styled to be a hamburger button – in this case the style for the hamburger button was taken from the UWP samples.

<Button Style="{StaticResource SplitViewTogglePaneButtonStyle}"
        Click="splitViewToggle_Click" />

private void splitViewToggle_Click(object sender, RoutedEventArgs e)
{
    RootSplitView.IsPaneOpen = !RootSplitView.IsPaneOpen;
}


// -------------------------------------- Page Resources ------------------------------

<Page.Resources>
    <ControlTemplate x:Key="SplitViewTogglePaneButtonTemplate"
                        TargetType="Button">
        <Grid x:Name="RootGrid"
                Background="{TemplateBinding Background}">
            <VisualStateManager.VisualStateGroups>
                <VisualStateGroup x:Name="CommonStates">
                    <VisualState x:Name="Normal" />
                    <VisualState x:Name="PointerOver">
                        <VisualState.Setters>
                            <Setter Target="RootGrid.Background"
                                    Value="{ThemeResource SystemControlBackgroundBaseLowBrush}" />
                            <Setter Target="ContentPresenter.Foreground"
                                    Value="{ThemeResource SystemControlHighlightBaseMediumHighBrush}" />
                        </VisualState.Setters>
                    </VisualState>
                    <VisualState x:Name="Pressed">
                        <VisualState.Setters>
                            <Setter Target="RootGrid.Background"
                                    Value="{ThemeResource SystemControlBackgroundBaseMediumLowBrush}" />
                            <Setter Target="ContentPresenter.Foreground"
                                    Value="{ThemeResource SystemControlHighlightBaseMediumBrush}" />
                        </VisualState.Setters>
                    </VisualState>
                    <VisualState x:Name="Disabled">
                        <VisualState.Setters>
                            <Setter Target="ContentPresenter.Foreground"
                                    Value="{ThemeResource SystemControlForegroundBaseLowBrush}" />
                        </VisualState.Setters>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>
            <ContentPresenter x:Name="ContentPresenter"
                                Padding="{TemplateBinding Padding}"
                                BorderBrush="{TemplateBinding BorderBrush}"
                                BorderThickness="{TemplateBinding BorderThickness}"
                                HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
                                VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
                                AutomationProperties.AccessibilityView="Raw"
                                ContentTemplate="{TemplateBinding ContentTemplate}"
                                ContentTransitions="{TemplateBinding ContentTransitions}" />
        </Grid>
    </ControlTemplate>
    <Style x:Key="SplitViewTogglePaneButtonStyle"
            TargetType="Button">
        <Setter Property="Background"
                Value="Transparent" />
        <Setter Property="Foreground"
                Value="{ThemeResource SystemControlForegroundBaseHighBrush}" />
        <Setter Property="BorderBrush"
                Value="{ThemeResource SystemControlForegroundBaseHighBrush}" />
        <Setter Property="BorderThickness"
                Value="0" />
        <Setter Property="Padding"
                Value="0" />
        <Setter Property="HorizontalAlignment"
                Value="Left" />
        <Setter Property="HorizontalContentAlignment"
                Value="Center" />
        <Setter Property="VerticalAlignment"
                Value="Top" />
        <Setter Property="VerticalContentAlignment"
                Value="Center" />
        <Setter Property="UseSystemFocusVisuals"
                Value="True" />
        <Setter Property="FontFamily"
                Value="{ThemeResource SymbolThemeFontFamily}" />
        <Setter Property="Content"
                Value="&#xE700;" />
        <Setter Property="Height"
                Value="48" />
        <Setter Property="Width"
                Value="48" />
        <Setter Property="FontWeight"
                Value="Normal" />
        <Setter Property="FontSize"
                Value="20" />
        <Setter Property="Template"
                Value="{StaticResource SplitViewTogglePaneButtonTemplate}" />
    </Style>
</Page.Resources>

Using the Universal Windows Platform SplitView Control with Visual States and BuildIt.Lifecycle

In this post I’m going to add a SplitView to the About page. I’ve also created two visual states that will control whether the Pane of the SplitView is visible or not (ie IsPaneOpen) and whether the display behaviour is for it to overlay or shift the content over (ie Inline). In the main content area I’ve also added two buttons which will be used to switch between each visual state. Lastly, in the SplitView.Pane has a Close button which will be used to return to the previous page in the application.

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
       <VisualStateManager.VisualStateGroups>
           <VisualStateGroup x:Name="SplitViewStates">
               <VisualState x:Name="Minimised" />
               <VisualState x:Name="Expanded">
                   <VisualState.Setters>
                       <Setter Target="RootSplitView.(SplitView.IsPaneOpen)"
                               Value="True" />
                       <Setter Target="RootSplitView.(SplitView.DisplayMode)"
                               Value="Inline" />
                   </VisualState.Setters>
               </VisualState>
           </VisualStateGroup>
       </VisualStateManager.VisualStateGroups>

       <SplitView  x:Name="RootSplitView">
           <SplitView.Pane>
               <Grid>
                   <Border Background="Yellow" />
                   <Button Click="CloseClick">Close</Button>
               </Grid>
           </SplitView.Pane>

           <StackPanel VerticalAlignment="Center"
                       HorizontalAlignment="Center">
               <TextBlock Text="About Page"
                          FontSize="40" />
               <TextBlock Text="{Binding AboutTitle}"
                          FontSize="40" />
               <Button Click="ExpandClick">Expand</Button>
               <Button Click="CollapseClick">Collapse</Button>
           </StackPanel>
       </SplitView>
   </Grid>
And here is the code behind for the page:

private void ExpandClick(object sender, RoutedEventArgs e)
{
    (DataContext as AboutViewModel).Expand();
}
private void CollapseClick(object sender, RoutedEventArgs e)
{
    (DataContext as AboutViewModel).Collapse();
}

And then the implementation of the Expand and Collapse methods on the AboutViewModel. For this we’re defining states whose names match the visual states I declared in the XAML. The Expand and Collapse method then switch between these states. I’ve shown the whole class (excluding the AboutTitle property as I’ve changed the base class to include the StateManager and OnCompletion.

public class AboutViewModel : BaseStateManagerViewModelWithCompletion<DefaultCompletion>
{
    public AboutViewModel()
    {
        StateManager.Group<AboutExpandStates>().DefineAllStates();
    }

    public void Close()
    {
        OnComplete(DefaultCompletion.Complete);
    }

    public void Expand()
    {
        StateManager.GoToState(AboutExpandStates.Expanded);
    }

    public void Collapse()
    {
        StateManager.GoToState(AboutExpandStates.Minimised);
    }
}

Lastly, I needed to modify the About state for the PrimaryRegion so that the Completion event would return the application to the Home state:

.StateWithViewModel<AppStates, AboutViewModel>(AppStates.About)
    .OnComplete(DefaultCompletion.Complete).ChangeToPreviousState()

    .WhenChangedToWithData<AppStates, AboutViewModel, string>((vm, d) => vm.AboutTitle = d)
.EndState();

And here is the output with the state manager triggering changes that switch visual states expanding and collapsing the Pane of the SplitView.

image image

Additional Windows using Regions with BuildIt.Lifecycle

In the first post on Getting Started with BuildIt.Lifecycle I touched on the notion of an application region being a logical representation of a Window that’s part of an application. Most Windows Phone, iOS and Android applications will only have a single region but the very nature of Windows lends itself to multi-windowed applications. In this post we’ll extend the sample application to launch a new Window by creating a new region within the application. We’ll start with the declaration of the region:

public enum AdditionalStates
{
    Base,
    Landing
}

public class AdditionalRegion : StateAwareApplicationRegion
{

    public AdditionalRegion()
    {
        StateManager.GroupWithViewModels<AdditionalStates>()
            .StateWithViewModel<AdditionalStates, LandingViewModel>(AdditionalStates.Landing)
                .OnComplete(DefaultCompletion.Complete)
                .CloseRegion(this)
            .EndState();
    }

    protected override async Task CompleteStartup()
    {

        await base.CompleteStartup();

        await StateManager.GoToState(AdditionalStates.Landing, false);
    }
}

You may notice this is slightly different from the PrimaryRegion I defined in my initial post as it inherits from the StateAwareApplicationRegion instead of just ApplicationRegion, and that there’s no StateManager property. The StateAwareApplicationRegion was added as a convenience class, which contains the StateManager property and implements the IHasStates interface. Note that both regions in my sample application now inherit from this class.

There is only a single state defined for the AdditionalRegion, which will correlate to the only page that will get loaded. When this region is started, the state manager transitions to the Landing state, which will display the LandingPage. In order for this to work, we need to create and correlate a LandingPage to the Landing state. I’ve added a page to the application based on the Blank Page template, called LandingPage. In App.xaml.cs I then needed to add the association between the new page and the Landing state:

LifecycleHelper.RegisterView<LandingPage>().ForState(AdditionalStates.Landing);

On the MainPage of the application I’ve added another Button which simply calls a new OpenAnotherWindow method on the MainViewModel.

private void AnotherWindowClick(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
    ViewModel.OpenAnotherWindow();
}

The OpenAnotherWindow method simply calls OnComplete with a new enumeration value of AnotherWindow (this enumeration value is independent of the fact that I’ll be using it to open a new window, I could have called it “ShowMeTheMoney” so long as it correlates to the handler I’ll define in the PrimaryRegion).

public void OpenAnotherWindow()
{
    OnComplete(MainCompletion.AnotherWindow);
}

As I just mentioned, I need to define what happens when OnComplete is called. To do this I need to update the state declarations in the PrimaryRegion. Here you can see the additional OnComplete which handles the AnotherWindow enumeration value, and calls LaunchRegion.

StateManager.GroupWithViewModels<AppStates>()
    .StateWithViewModel<AppStates, MainViewModel>(AppStates.Home)
        .OnCompleteWithData(MainCompletion.Settings,vm=>vm.HelloWithTime)
        .ChangeState(AppStates.Settings)

        .OnCompleteWithData< AppStates, MainViewModel,MainCompletion,string>(MainCompletion.About,null)
        .ChangeState(AppStates.About)
                   
        .WhenChangedTo(async vm => await vm.LoadData())
                   
        .OnComplete(MainCompletion.AnotherWindow)
        .LaunchRegion(this,TypeHelper.Ref<AdditionalRegion>())
    .EndState()

Note: The TypeHelper is there to try to eliminate the need to specify all the Type arguments for the LaunchRegion method. I’ll look to refactor this more as we go to make it less obtuse to use.

That’s it, when you run the application, clicking the new Button will spawn of new windows (and actually will keep spawning new windows each time you click the button, even if the previous window is still open).

Refactoring Data Passing Methods in BuildIt.Lifecycle

In my earlier post, Passing Data Between States with BuildIt.Lifecycle, the state declarations for the primary application region started to look really messy. As promised I’ve tidied up the extension helper methods, making it much easier to define delegates that are invoked when changing to and from a state. The updated state declartion now looks like:

StateManager.GroupWithViewModels<AppStates>()
    .StateWithViewModel<AppStates, MainViewModel>(AppStates.Home)
        .OnCompleteWithData(MainCompletion.Settings,vm=>vm.HelloWithTime)
            .ChangeState(AppStates.Settings)
        .OnCompleteWithData< AppStates, MainViewModel,MainCompletion,string>(MainCompletion.About,null)
            .ChangeState(AppStates.About)
        .WhenChangedTo(async vm => await vm.LoadData())
    .EndState()
               
    .StateWithViewModel<AppStates, SettingsViewModel>(AppStates.Settings)
        .OnComplete(DefaultCompletion.Complete).ChangeToPreviousState()
        .WhenChangedToWithData<AppStates, SettingsViewModel, string>((vm, d) => vm.SettingsTitle = d)
    .EndState()
               
    .StateWithViewModel<AppStates, AboutViewModel>(AppStates.About)
        .WhenChangedToWithData<AppStates, AboutViewModel, string>((vm, d) => vm.AboutTitle = d)
    .EndState();

Note that if you’re attempting to use these updates they haven’t yet made their way into the NuGet packages. I’ll endeavour to update these packages later this week. In the meantime I’d recommend downloading the source from the BuildIt repository on GitHub.

Loading Data Using Services with BuildIt.Lifecycle

The BuildIt.Lifecycle automatically creates instances of ViewModels and associates them with the corresponding page/view as the user steps between application states. Behind the scenes this uses Autofac to create these instances and ensure any dependencies are resolved. ViewModels that require access to device hardware, or to services that load or save data, can simply include the interface in the constructor. Take the following example where the MainViewModel relies on an instance of the IDataService interface to populate the Contacts collection.

public interface IDataService
{
    Task<IEnumerable<string>> LoadContacts();
}

public class MainViewModel : BaseViewModelWithCompletion<MainCompletion>
{
    public ObservableCollection<string> Contacts { get; }=new ObservableCollection<string>();
    private IDataService Data { get; }
    public MainViewModel(IDataService data)
    {
        Data = data;
    }
    public async Task LoadData()
    {
        var contactList = await Data.LoadContacts();
        contactList.DoForEach(Contacts.Add);
    }

We need to make two changes to our application declaration. The first is to register the implementation of IDataService, which we’ll do in App.xaml.cs

var core = new SimpleStatesApplication();
var wm = new WindowManager(core);
await core.Startup(builder =>
{
    builder.RegisterType<UWPDataService>().As<IDataService>();
});

public class UWPDataService : IDataService
{
    public async Task<IEnumerable<string>>  LoadContacts()
    {
        return new []{"Joe", "Bob", "Frank"};
    }
}

The other change is to then invoke this method when the MainViewModel is used, this is when the Home state is changed to. We add the following to our state declarations

(group.Item2.States[AppStates.Home] as IViewModelStateDefinition<AppStates, MainViewModel>)
    .WhenChangedTo(async vm => await vm.LoadData());

Note that we can also register services as part of our application class, allowing them to be registered within the PCL, rather than the platform specific libraries. This is useful for services that you want to use the same implementation for all supported platforms.

public class SimpleStatesApplication : RegionAwareBaseApplication<PrimaryRegion>
{
    protected override void RegisterDependencies(ContainerBuilder builder)
    {
        base.RegisterDependencies(builder);
        builder.RegisterType<BasicDebugLogger>().As<ILogService>();
    }
}

The ILogService is used within BuildIt.Lifecycle, so registering this implementatino will allow you to view the diagnostic information in the Output window within Visual Studio.

Passing Data Between States with BuildIt.Lifecycle

Yesterday I discussed in my post, Navigating Back using State History with BuildIt.Lifecycle, how we needed to extend the state manager concept to understand and track state history. The next challenge that app developers face, that doesn’t fit nicely into the current state manager concept is that you often need to pass data between pages of the application. Think of a simple example where you have a list of contacts on one page; the user taps on one of those contacts and is taken to a new page that shows the details of that person. What you need to pass between pages is information about which contact the user tapped. I’ll discuss later how we can pass data between states, and thus view models, later in the post, but first I wanted to dig into the concept of passing data between pages a bit further.

When the user tapped on the contact in the list, there is a lot of information that we can record about that event:

- Mouse/Touch position (don’t forget we’re not just dealing with mobile devices, apps are a big part of the desktop experience on Mac and PC now)

- List index (the index of the item tapped in the list)

- List item (the object in the list that corresponds to the cell that was tapped)

- Item Id (some sort of unique identifier of the item that was tapped)

With all these pieces of information, the question is which one to pass to the details page. Let’s review them individually:

- Mouse/Touch position – this in itself isn’t very useful as it only has meaning on the page that’s currently in view and that point in time. For example, if the user were to scroll the list, all of a sudden this information is useless as it now points to a different cell in the list

- List index – this is only useful if the details page has a reference to exactly the same list that is used to populate the contacts list page. If the underlying data set changed (eg sync with backend caused new contacts to be downloaded), then the list index might now point to a different item

- List item – if the items in the list contain all the details necessary to populate the details page then passing the list item to the details page is ok. However, if you have a large list off contacts, chances are you only load enough data about each contact to display the item in the list (eg perhaps only photo and name). In this case, passing the whole entity isn’t that useful since the details page has to load the whole contact anyhow. The other point worth considering is about serialization of navigation parameters – depending on the platform there are mechanisms for persisting navigation stack, including data parameters; some of these have size and complexity constraints on the data passed between pages.

- Item Id – this is probably the best choice for this scenario. It contains enough information for the contact to be loaded. If performance is a concern (ie you want the contact summary information to be instantly available when the user arrives at the details page, even if details have to be progressively loaded), then you should cache the contact summary information in memory in a service which can be accessed as soon as the details page is loaded. The cache service might also cache the last X number of loaded contacts in memory to make it quick for a user to go between contacts.

Ok, so this works for this scenario but how do we summarize this into a more generic form. Essentially it comes down to passing the minimal set of information between pages that is required to completely load the next page. Ideally pages (and their corresponding ViewModel) should be self sufficient, relying only on application services to load data. Application services, most likely passed into the ViewModel using some sort of dependency injection, provide an abstraction over loading/saving data both locally (in memory and to disk) and across the network (calling services or performing data sync). To this end, by supplying the minimum set of information (eg item id in the case of the contacts list scenario), the ViewModel is only reliant on that piece of information. This reduces the necessary testing surface area as there are few permutations of input data that need to be teted.

Now that we have an understanding of how to determine what data to pass between pages, we again need to come back to thinking in terms of states and passing data during a transition between states. If you recall, our ViewModels aren’t aware of states or the ability to change between them. All they’re able to do is indicate that they’re complete. In the following code, the MainViewModel exposes a public HelloWithTime property as well as a private AboutWithTime property, the former will be passed into the Settings state, the latter into the About state. You’ll observe that the OnComplete call for settings hasn’t been changed, whilst there is now a call to OnCompleteWithData, passing in the AboutWithTime property value, for completing with the About completion value.

public class MainViewModel : BaseViewModelWithCompletion<MainCompletion>
{
    public string HelloWithTime => $"Hello World at {DateTime.Now.ToString("h:mm tt")}";
    private string AboutWithTime => $"About information {DateTime.Now.ToString("h:mm tt")}";

    public void DisplaySettings()
    {
        OnComplete(MainCompletion.Settings);
    }
    public void DisplayAbout()
    {
        OnCompleteWithData(MainCompletion.About,AboutWithTime);
    }
}

Our state declaration now looks like the following:

var group = StateManager.GroupWithViewModels<AppStates>()
    .StateWithViewModel<AppStates, MainViewModel>(AppStates.Home)
        .OnCompleteWithData(MainCompletion.Settings,vm=>vm.HelloWithTime)
            .ChangeState(AppStates.Settings)
        .OnCompleteWithData< AppStates, MainViewModel,MainCompletion,string>(MainCompletion.About,null)
            .ChangeState(AppStates.About)
        .EndState()
    .StateWithViewModel<AppStates, SettingsViewModel>(AppStates.Settings)
        .OnComplete(DefaultCompletion.Complete).ChangeToPreviousState()
        .EndState()
    .StateWithViewModel<AppStates, AboutViewModel>(AppStates.About)
        .EndState();

(group.Item2.States[AppStates.Settings] as IViewModelStateDefinition<AppStates, SettingsViewModel>)
    .WhenChangedToWithData<AppStates, SettingsViewModel, string>((vm, d) => vm.SettingsTitle = d);

(group.Item2.States[AppStates.About] as IViewModelStateDefinition<AppStates, AboutViewModel>)
    .WhenChangedToWithData<AppStates, AboutViewModel, string>((vm, d) => vm.AboutTitle = d);

What’s interesting here is that there are two parts to passing data between states. The first is how the data is exposed out of the state that’s being completed. Here I’ve shown two ways – completion with Settings uses the public HelloWithTime property to retrieve the data to pass to the next state; completion with About can’t access the AboutWithTime property, since it’s private, which is why it relies on the MainViewModel to pass out the AboutWithTime value when it invokes OnCompleteWithData. Note that currently in the state declaration you still need to call OnCompleteWithData passing in a null value for the data accessor, otherwise the data value isn’t passed – this will be fixed shortly.

The second part of passing data between states is how the data is passed into the new state. This is done by calling the WhenChangedToWithData and specifying a function that will be invoked with the corresponding data. Note this method is a generic method and you can actually call it multiple times with different data types, which means that you can handle arriving at this state with different data (perhaps from different states). One thing to be aware of is that behind the scenes the data is being serialized to json, so whatever data you pass between states need to be serializable to json. Ideally the data being passed between states should be kept to a minimum.

Navigating Back using State History with BuildIt.Lifecycle

So far I’ve only discussed transitioning between states with an assumption that every transition is forward looking and that our state manager doesn’t track any sort of history. Other than simple applications, or poorly architected applications that don’t make use of the devices back navigation feature, it is a requirement for most applications to track the page/view/state that the user was previously in, allowing them to easily navigate back to where they just came from. This doesn’t fit into the normal concept of a finite state machine, since this wouldn’t normally have any “memory”. However, our state manager can easily be extended to include a history of which states have been visited.

Again, let’s start with updating the user interface on our simple application. On the settings page we didn’t have any way to get back to the main/home page of our application. I’ll add a new button, resulting in the following XAML and codebehind:

<Page x:Class="SimpleStates.Pages.SettingsPage"
      xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:SimpleStates.Pages"
      xmlns:d="
http://schemas.microsoft.com/expression/blend/2008"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      mc:Ignorable="d">
    <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <TextBlock Text="Settings Page"
                   VerticalAlignment="Center"
                   HorizontalAlignment="Center"
                   FontSize="40" />
        <Button Click="GoBackClick">Go Back</Button>
    </StackPanel>
</Page>

public sealed partial class SettingsPage
{
    public SettingsPage()
    {
        InitializeComponent();
    }
    private void GoBackClick(object sender, RoutedEventArgs e)
    {
        (DataContext as SettingsViewModel).Complete();
    }
}

The only change I needed to make to the SettingsViewModel is to change the back class. Note that in this case since there is only one way that the state can be exited, I’m going to use the built in DefaultCompletion enumeration, which has a single Complete value.

public class SettingsViewModel : BaseViewModelWithCompletion<DefaultCompletion>
{
    public void Complete()
    {
        OnComplete(DefaultCompletion.Complete);
    }
}

The only thing left to do is to update the state declaration for my application to change to the previous state when the Settings state is Complete, as follows:

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)
        .OnComplete(DefaultCompletion.Complete).ChangeToPreviousState()
        .EndState()
    .StateWithViewModel<AppStates, AboutViewModel>(AppStates.About);

The state history feature does allow for skipping states that are in the history, so you can easily return to the start of an application by changing to the corresponding state using ChangeBackToState instead of ChangeState.

Note: The state history feature can be turned on/off on individual state groups. By default TrackHistory is disabled for regular state groups. State groups that have corresponding view models has TrackHistory enabled by default.

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!

Navigation using BuildIt.Lifecycle

In my post, Getting Started with BuildIt.Lifecycle, I showed how to get started with BuildIt.Lifecycle. The next step is to add basic navigation between pages/views. To do this, we need to change our mindset to again think of this as a state transition. Before I cover what I mean by this, let’s add two buttons to our Home page that we’ll use to trigger the navigation to either the Settings or About pages.

<StackPanel VerticalAlignment="Center"
            HorizontalAlignment="Center">
    <TextBlock Text="Main Page" FontSize="40" />
    <Button Click="SettingsClick">Settings</Button>
    <Button Click="AboutClick">About</Button>
</StackPanel>

As you can see from the XAML there are event handlers wired up to the Click event on each of the Buttons. In each of the event handlers we simply route a call to a corresponding method on the ViewModel.

private MainViewModel ViewModel => DataContext as MainViewModel;

private void SettingsClick(object sender, RoutedEventArgs e)
{
    ViewModel.DisplaySettings();
}

private void AboutClick(object sender, RoutedEventArgs e)
{
    ViewModel.DisplayAbout();
}

Now we need to think about what displaying either the Settings or the About page means in the context of our application state. When the application is launched the PrimaryRegion transitions to the AppStates.Home state, which correlates to the HomePage being displayed. When the user clicks on either button, what we want the application to do is to transition from the Home state to the either the Settings or About state, depending on which button the user clicks.

Unlike some other frameworks where you can navigate from one ViewModel to another ViewModel by calling a base method such as ShowViewModel or call Navigate on some sort of Navigation wrapper, the HomeViewModel doesn’t actually know anything about the states of region (this is the responsibility of the StateManager that’s part of the PrimaryRegion). Instead, what the HomeViewModel has to do is to raise an event signalling that an event has occurred that it needs assistance dealing with. So the implementation for DisplaySettings and DisplayAbout becomes:

public event EventHandler ShowSettings;
public event EventHandler ShowAbout;
public void DisplaySettings()
{
    ShowSettings.SafeRaise(this);
}
public void DisplayAbout()
{
    ShowAbout.SafeRaise(this);
}

Note: SafeRaise is a helper method defined in BuildIt.General but with the new features in C# 6 you could also write this as:

ShowAbout?.Invoke(this,EventArgs.Empty);

With the HomeViewModel raising these events, we need to subscribe to them somewhere so that the PrimaryRegion can trigger the state change on its StateManager to go to either the Settings or About states. This is done by wiring and unwiring event handlers as part of the state transitions into and from the Home state:

sm.DefineViewModelState<MainViewModel>(AppStates.Home)
    .WhenChangedTo(vm =>
    {
        vm.ShowSettings += ChangeToSettings;
        vm.ShowAbout += ChangeToAbout;
    })
    .WhenChangingFrom(vm =>
    {
        vm.ShowSettings -= ChangeToSettings;
        vm.ShowAbout -= ChangeToAbout;
    });

And then it’s just a matter of implementing the ChangeToSettings and ChangeToAbout methods which just need to invoke a state change on the StateManager.

private async void ChangeToAbout(object sender, EventArgs e)
{
    await StateManager.GoToState(AppStates.About);
}

private async void ChangeToSettings(object sender, EventArgs e)
{
    await StateManager.GoToState(AppStates.Settings);
}

And we’re done; now when the user clicks on either the Settings or the About buttons, the PrimaryRegion will trigger the state change, which will in turn navigate to the corresponding page.

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

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

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

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

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"
      xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      xmlns:mc="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

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.

Cross Platform Visual States

Last year I posted on “Taking Visual States Cross Platform to iOS and Android with Xamarin” and is a topic that I often come back when discussing the design and development of mobile applications. Let’s start by discussing what visual states are and why they’re important when building applications (and this really applies to any application, not just mobile applications). During the design of an application it’s common to prepare wireframes and visual designs that document each page, the key elements on each page and any associated behaviour. This typically includes documenting when elements are hidden or shown, often in response to either data changes or user interactions. The following screenshots show a recent application we worked on for Hungry Jack’s for Windows 10. This is the same page of the same application, running on the same device, just with a different window size. As you can see the difference between the first two images is quite significant as the navigation bar switches from being at the bottom (similar to what you’d expect for mobile) to on the left side. The third image simply augments the position of elements further to make better use of the available screen size.

image image image

Thorough analysis during the design phase will reveal all possible layout combinations for a page; these combinations are what we refer to (at least in the Windows/XAML world) as visual states. In the case of the screenshots from the Hungry Jack’s application, each of these layouts represents a different visual state for this page. If you don’t take the time to determine what visual states exist on each page and what triggers a transition between visual states, during development you’ll find yourself toggling attributes on element on the page in an attempt to recreate each required combination. This lack of a structured approach makes it not only hard to layout each page, it also makes it hard to test as there is no definitive list of layouts that need to be verified.

On the Windows platform, Visual States are something we take for granted; they can be declared in XAML and Blend has support for designing each visual state. However, other platforms are not so blessed and have to resort to changing attributes manually in code. Some cross platform technologies make use of data binding to allow visual elements to be dynamically updated based on changes in the corresponding data (ie the view model). These include MvvmCross and Xamarin.Forms. However, data binding should be reserved for updating data values on a view, not controlling the visual states on a page.

Learning to develop for the Windows platforms, developers go through a series of learning steps.

  • Coding Changes: Most developers come from building applications or web sites where they’re used to having to set data values in code.
  • Data Binding: The first step along the progression is learning how to use data binding to update content on the page (eg Text on a Textblock)
  • MVVM: After seeing the benefit of data binding, the next step is to appreciate the separation of concerns that MVVM offers. At this point developers often look at what MVVM libraries there are out there and usually settle on something like MvvmCross, MvvmLight, Caliburn.Micro, Prism etc
  • Converters: Equipped with the new found power of data binding, developers often go nuts and start data binding everything, including using properties on the view model to control when items should be visible. This is where they look to use converters to adapt properties on the view model (eg XYZIsVisible which would be a bool) to attributes on visual elements (eg XYZ.Visibility which is a Visibility). The issue with this is that at design time, in a tool like Blend, it’s very difficult to see what the layout looks like. You can’t simply change the Visibility property on elements, since they’re now data bind. You can temporarily remove the data binding, but then of course you forget to put it back and then spend hours trying to work out why the application is broken.
  • Visual States: Enter Visual States…. instead of data binding attributes that control the layout of a page, it’s better to use visual states to define what elements are visible and any layout changes required for a particular layout. Blend supports design time editing of visual states and the ability to visualize any combination of visual states from different state groups
  • View Model States: Eventually developers realise that not only should they use visual states, they should track and control them from their view model, thus making yet another aspect of their application testable. I’ve talked about this a couple of times (http://nicksnettravels.builttoroam.com/post/2015/08/10/application-development-using-states-and-transitions.aspx, http://nicksnettravels.builttoroam.com/post/2014/01/11/Visual-States-in-Windows-Phone-and-Windows-Applications-using-MvvmCross.aspx, http://nicksnettravels.builttoroam.com/post/2014/05/19/Taking-Visual-States-Cross-Platform-to-iOS-and-Android-with-Xamarin.aspx)

Ok, so now that you have the basics on what a visual state is, and some background on why I believe visual states are so important, let’s discuss the elephant in the room….. Visual States only exist in XAML on the Windows platform…. making it very difficult to use visual states when building cross platform applications. So, what can we use when building cross platform? Well let’s go through the progression that developers go through. As you’d expect, all platforms support developers being able to adjust values via code. Unfortunately, this is where most developer technologies end, for example neither iOS (Objective-C, Swift) or Android (Java) support data binding out of the box. There are some third party solutions that attempt to bridge this gap, such as the data binding support in MvvmCross and Xamarin.Forms. In fact both these options provide not only the ability to data bind, but also enable MVVM and support using converters as part of data binding.

In actual fact there’s no requirement to have data binding (and subsequently MVVM and the use of converters) in order to start using visual states to control layout. However, again there’s almost no platform, or even third party, support for defining visual states.Over the weekend, I was experimenting with Xamarin.Forms and was thinking about how to define and transition between visual states. Whilst it would be nice to do it declaratively in XAML, I thought I’d better walk before I run, so I figured I’d work out a way to define visual states in code. Before getting started I thought though the basic mechanics of how visual states should work:

- Visual States should be declared in groups, and each group can only have one active visual state at any given time

- A visual state should define any number of value actions

- A “value action” defines setting a property on an element to a specific value

- The visual state manager should be able to change to a specific visual state

- Changing to a specific visual state, should only adjust the current state of the group that the visual state belongs

I’ve always felt that one of the weaknesses of Visual states on the XAML platform is that they’re named using a string, and the only way to reference them when changing state, is using a string literal. So, for my attempt at a visual state manager I’m going to have my visual states defined as an enumeration. In fact, each group of states will use a different enumeration type – thus each visual state corresponds to a unique enumeration value. The end game is to be able to declare visual states in a relatively fluid manner, as shown in the following example which defines two groups based on the enumerations SecondStates and SecondStates2.

VisualStateManager
    .Group<SecondStates>()
        .DefineState(SecondStates.State1)
        .DefineState(SecondStates.State2)
            .Target(textBlock)
                .Change(x => x.TextColor, (x, c) => x.TextColor = c)
                .ToValue(Color.FromHex("FFFF008B"))
            .Target(textBlock)
                .Change(x => x.FontSize, (x, c) => x.FontSize= c)
                .ToValue(40)
        .DefineState(SecondStates.State3)
            .Target(textBlock)
                .Change(x => x.TextColor, (x, c) => x.TextColor = c)
                .ToValue(Color.FromHex("FFFFC500"))
            .Target(textBlock)
                .Change(x => x.FontSize, (x, c) => x.FontSize = c)
                .ToValue(10)
        .DefineState(SecondStates.State4)
    .Group(SecondStates2.Base)
        .DefineState(SecondStates2.StateX)
        .DefineState(SecondStates2.StateY)
            .Target(textBlock2)
                .Change(x => x.TextColor, (x, c) => x.TextColor = c)
                .ToValue(Color.FromHex("FFFF008B"))
            .Target(textBlock2)
                .Change(x => x.FontSize, (x, c) => x.FontSize = c)
                .ToValue(40)
        .DefineState(SecondStates2.StateZ)
            .Target(textBlock2)
                .Change(x => x.TextColor, (x, c) => x.TextColor = c)
                .ToValue(Color.FromHex("FFFFC500"))
            .Target(textBlock2)
                .Change(x => x.FontSize, (x, c) => x.FontSize = c)
                .ToValue(10);

In my next post we’ll look at the different classes that make up the visual state manager and the extension methods that allow for the fluid declaration seen in this example.

SplitView Bug in the Universal Windows Platform (UWP)

Last week we were trying to resolve an issue that had been raised in an application we were building for the Universal Windows Platform, where the user had to press the back button twice in order to navigate back to the previous page. Initially we thought that we must be subscribing to the hardware back button on Windows 10 Mobile, as the issue seem to only manifest itself on mobile. However, after eliminating all calls to subscribe to the back button, we were still seeing the same issue. After a bit of further testing the issue only seemed to arise if we were navigating to a new page with the pane of the SplitView visible, but not always…. Eventually we tracked it down to the scenario where the DisplayMode is set to Overlay and the pane of the SplitView is visible (ie IsPaneOpen set to true). Navigating to a new page without first either changing the DisplayMode to Inline, or closing the pane, will result in the pane staying open, and unfortunately it still keeps the event handler on the BackRequested event on the SystemNavigationManger.

SplitViewFail sample

Simple solution: make sure that you force the pane closed before navigating by setting IsPaneOpen to false (assuming the DisplayMode is set to Overlay).

Ignite Session Recordings

Earlier this month I delivered two sessions at Ignite Australia. The recording for these sessions are now available for watching

Application Development for the Universal Windows Platform
This session provides an overview of the Universal Windows Platform (UWP), a single platform that developers can target to build applications that will run across a myriad of devices. After years of progressive convergence, UWP offers a single application model, a single developer experience and a single store. The session will discuss building applications that can run on any device, whilst still being able to leverage the individual device features. It'll provide a roadmap for tools, frameworks, and guidance on building for the Universal Windows Platform.


Bare Metal Development with the Universal Windows Platform
Ignite Australia
The Universal Windows Platform offers developers a unique opportunity to build applications that run on an extensive range of devices. However, this comes the challenge of how to scale the user experience and how to adapt to different device capabilities. This session will dig deep into the use of Visual States, adaptive triggers, device family and feature detection, new binding syntax and new controls.

Changing DataContext and Compiled Data Binding in Universal Windows Platform Applications

Back in April I talked briefly about Compiled Data Binding and how it improves performance throughout your XAML application by eliminating all the reflection calls that go on in the background with traditional data binding. Recently I've been spending more time investigating the best way to structure code and design time data to ensure high quality applications. One of the easy pitfalls with the x:Bind syntax is a lack of understanding of the context of the data binding.

To start with, let's recap how traditional data binding works: Elements on the page have a DataContext, which you can think of as the object that is being data bound to. The DataBinding expression includes a Path, which determines the property on the DataContext that a particular attribute on an element is being data bound to. By default the DataContext flows down the page/usercontrol as each element inherits the DataContext from its parents. However, it is possible to override the DataContext by setting it explicitly for each element. There are plenty of sites/pages that can provide more detail on this form of data binding.

The new compiled data binding, which I'll refer to as x:Bind, tries to eliminate any use of reflection at runtime. In order to do this, the compiler generates a large quantity of code to connect the data entities and the visual elements. This means that the compiler must know Type information about both the attribute on the visual element, and the properties on the element being data bound. The starting point is that the context for x:Bind expressions on a Page is the Page itself, and similarly for a UserControl, it is the UserControl itself. If we assume that the data we want to data bind to is within our ViewModel, then we need to expose the ViewModel as a property on the Page. For example:
public MainViewModel CurrentViewModel => DataContext as MainViewModel;
and then in the XAML for the Page it can be referenced as follows:
<TextBlock Text="{x:Bind CurrentViewModel.Name, Mode=OneWay}" />
This references the Name property on the MainViewModel entity returned by the CurrentViewModel property on the page. What's not immediately obvious here is that despite indicating that the Mode is OneWay (ie it should detect changes) there is no code that notifies the data binding framework if the CurrentViewModel changes. This could happen if the DataContext changes, for example in the following code where the OnNavigatedTo method has a small latency before it sets the DataContext.
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
    base.OnNavigatedTo(e);
    await Task.Delay(2000);

    DataContext = new MainViewModel();
}
In this case, the underlying DataContext is updated after 2 seconds to the new instance of the MainViewModel. This means that the CurrentViewModel value has also changed. However, there has been no attempt to notify the data binding framework, and subsequently the page, of this change. There are a number of ways to address this issue, the two I'm going to present here both rely on the Page implementing the INotifyPropertyChanged interface, exposing an event PropertyChanged, which the data binding framework will listen for.

Option 1 - Raise the PropertyChanged event manually after setting the new DataContext eg
DataContext = new MainViewModel();
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("CurrentViewModel"));
Option 2 - Intercept the DataContextChanged event and then raise the PropertyChanged
public MainPage()
{
    InitializeComponent();

    DataContextChanged += MainDataContextChanged;
}
private void MainDataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args)
{
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("CurrentViewModel"));
}
Both of these options use a hardcoded property name, CurrentViewModel, which is not recommended. A last option, that might appear more complex to begin with, has the advantage of being simple, clean and less riddled with string constants. It starts with a Wrapper class, which provides a level of indirection between the Page and the ViewModel:
public class Wrapper<T>:INotifyPropertyChanged
{
    public T Entity { get; set; }
    public event PropertyChangedEventHandler PropertyChanged;

    public Wrapper(FrameworkElement element)
    {
        element.DataContextChanged += Element_DataContextChanged;
    }

    private void Element_DataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args)
    {
        if (args.NewValue is T)
        {
            Entity = (T) args.NewValue;
        }
        else
        {
            Entity = default(T);
        }
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Entity"));
    }

    public virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
And using this entity is simple as you just need to define the type of ViewModel:
public Wrapper<MainViewModel> Data => new Wrapper<MainViewModel>(this);
The only other difference is that there is an extra level of indirection in the x:Bind expression.
<TextBlock Text="{x:Bind Data.Entity.Name, Mode=OneWay}" />

Now as the DataContext on the Page changes the Wrapper class will detect the change and raise the PropertyChanged event indicating that the "Entity" has changed. Since x:Bind connects to any object in the binding path that implements INotifyPropertyChanged, it will handle this event and look for the updated Entity property, which will be the new DataContext value.

As you start to work with x:Bind, you will notice that your binding expressions are longer than you'd be used to. Don't worry about this, since the data binding itself will be orders of magnitude quicker


Certifying and Distributing Windows Store Applications to Earlier (ie Non-Supported) Versions of Windows 10 Mobile

In my previous post on Visual Studio 2015 RTM, Universal Windows Platform (UWP) Developer Tools and Debugging on a Windows Phone device I covered modifying the TargetPlatformMinVersion property in the Visual Studio project file in order to deploy your Universal Windows Platform application to a Windows 10 Mobile device. Granted that this post is probably a little too late, since the next build of Windows 10 Mobile is now out (I'm yet to install it), but it is possible to publish your application to the Store so that it can be installed on a non-developer enabled phone.

Now you'd think you'd just be able to create a Store package with the TargetPlatformMinVersion property set to the required value (in this case 10.0.10166.0 to match what I currently have on my device - see the OS Version attribute in the About screenshot below). Unfortunately this is not the case!


If you do attempt to generate a Store package you'll see an error similar to the following image - "10.0.10166.0" is not a supported value for TargetPlatformMinVersion. Please change it on the Project Property page



Ok, so step one is to revert the change to TargetPlatformMinVersion so that you can at least generate a Store package. From the Project menu in Visual Studio, select Store -> Create App Packages (if this option isn't enabled, make sure you have you UWP project selected in Solution Explorer). Follow the prompts to do a Store build - this should generate an .appx file you can work with.

What we're going to do is manually modify the generated package. From the Start menu, select the Developer Command Prompt for VS2015



Change directories to where the .appx file resides. Execute the following command, substituting you .appx filename. This will extract the contents of the .appx file into a sub-folder called "export"
makeappx unpack /nv /p myapplicationpackage.appx /d .\export
Open the AppxManifest.xml file using your text editor of choice. Locate the line that looks similar to the following and replace the MinVersion with the version number you want to target (ie 10.0.10166.0)

<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.10240.0" MaxVersionTested="10.0.10240.0" />
Close and save the updated AppxManifest.xml file. And return to the command prompt and execute the following (again feel free to replace the .appx filename)
makeappx pack /nv /d .\export /p myapplicationpackage-updatedversion.appx
And you're done - submit this package to the Store, and a couple of days later (yes, whilst certification only seems to take a couple of hours, the distribution of UWP apps is very slow at the moment, so expect to wait at least 24hours from when certification is completed) you'll be able to install your application...... of course, all of this could have been saved if you'd just updated to the latest Windows 10 Mobile build, which I'm about to do now!