Caliburn.Micro Xaml made easy

Application State, part 3

In my previous post I talked about how Caliburn.Micro deals with state on Windows Phone (Silverlight). Today I want to deal with the differences in WinRT (Windows 8 and Windows Phone 8.1) and how it impacts things. I want to finish with some potential solutions and open it up to discussion.

WinRT Frame View Caching

I’ve already talked about how the view caching behaviour of PhoneApplicationFrame on Windows Phone (Silverlight) enables almost all the view state and view model state outcomes we’re expecting.

However the Frame control in WinRT has a largely different behaviour which can trip a lot of developers (including myself) up.

The Page control in WinRT has a property NavigationCacheMode which by default is set to Disabled. This means on every navigation a new view is created, including navigating back to a view you were previously on. This means we instantly lose any view and view model state upon navigation. You can see this happening in a brand new application where you scroll down a list, select and item to take you to another page. Upon navigating back you’ll notice the ScrollViewer will be reset back to the start. Not desirable at all.

So what happens when we set NavigationCacheMode to Enabled?. Initially it looks like we get back to our desired behaviour, navigating back to our previous views restores a cached view rather than a new one. This view held our view model so view and view model state are retained.

The problems surface when you navigate forward again. For our first example lets consider an application with two views, Product List and Product Details. If the user taps a product and the Frame navigates from Product List to Product Details. A new Product Details view and view model are created as expected, navigating back to Product List returns to the cached view / view model pairing with retained state.

Where it all goes wrong however is when the user taps a second product and the Frame navigates to Product Details with a different parameter. The Frame will reuse the cached view from the previous product. This means you’ll have the same view model from the previous product details as before as well. Unless you’re careful with your views the user will see a flash of the previous product details before the new product data loads.

This gets even worse if your application can potentially have the same view in the stack multiple times (for instance a file browser application may have a Browse Folder view). In this case with caching on then navigating backward or forward will be to the view / view model you were previously on and by loading the new state means you’re wiping out the state for the previous view so you get no benefit at all.

To summarise, the caching behaviour of the Frame respects neither the type of navigation (Back, Forward, New) nor the parameter of the navigation. This is no where near our desired behaviour and puts a lot more onus on us as application developers to retain the correct state.

WinRT Frame State

Frame does have one set of useful methods, GetNavigationState and SetNavigationState. GetNavigationState returns a serialized representation of the forward and back stacks at the time of calling. It’s a custom format that you’re not supposed to understand, although some people have posted articles on manipulating it. One caveat is that this custom format can’t handle complex types when used as a navigation parameter and will throw an exception if they’re used. Simple types such as string and integer are fine

If you’re using INavigationService.UriFor<> with simple types then you shouldn’t run into this problem.

We can use these methods to save frame state on suspension of the application, so if the application is terminated when can restore it to where the user was previously.

This functionality already exists in Caliburn.Micro and is usable through the methods INavigationService.SuspendState and INavigationService.ResumeState. These take the current frame state and store it in ApplicationData.Current.LocalSettings to be restored if required.

You can see a sample of this in the Windows 8.1 Sample.

Potential Solutions

Selective Caching

For the Product List / Product Details example above we can get our desired behaviour by only enabling NavigationCacheMode on the Product List view and not Product Details. This only works because we only have two levels of view, if for instance Product Details navigated forward to say Product Photo Gallery then this isn’t a plausible solution as we’ll want to enable caching on Product Details. We then run into the problems highlighted above.

For our File Browser example it makes sense to have NavigationCacheMode set to Disabled as we’re unable to retain any state at all given that it’s wiped out by the next view model on the stack. This doesn’t mean we get any of our desired behaviour, just lessen the amount of work we need to do to avoid a sub-optimal user experience.

Suspension Manager

The Visual Studio templates for Windows 8 and Windows Phone 8.1 come with a class SuspensionManager than can help with some of these problems. First it gives us the ability to save the frame state of an application to allow for suspension and resumption of the application. It also maintains a “stack” of state for you. Because the default templates have a rather weird implementation of MVVM then you can use this state bucket for both view and view model state.

This avoids the requirement of enabling caching on the Frame, but pushes the responsibility back to you as a developer to determine which view / view model state your want to retain (including minor things like scroll position). This can get awkward when the ScrollViewer is built into a GridView and you can’t restore the scroll position until the layout is updated and so on. It’s a passable solution, but becomes onerous quickly and prone to mistakes.

Custom Frame

It’s occurred to me a number of times while ranting about this problem to create my own Frame control that acts in the same way as PhoneApplicationFrame from Windows Phone (Silverlight). It’s certainly a possibility, but in my opinion on out of the scope of Caliburn.Micro in mandating control usage. Particular care would need to be paid around performance in caching views.

This is the best solution for retaining view state on Frame navigation.

I still have hope some of this will be resolved in Windows 10.

Maintaining a View Model stack

An idea I’ve been experimenting with is having the Caliburn.Micro navigation service maintaining internal stacks of view models mapping one to one with the forward and back stacks of the Frame. On navigating backwards a new view is created but an existing view model is reused. This would give us view model state retained over navigation. It’s working well in the experiments, but is running afoul of a weird xaml bug and cached views it would not help in the retaining of view state.

StorageHandler<> for WinRT

As quickly discussed in the previous post Caliburn.Micro has a solution to retain view model state on the current view model when the application is suspended through a class named StorageHander<> (you can read more about it in the documentation). Porting this to WinRT is certainly a possibility, I have had some feedback the current API isn’t to everyone’s liking and it’s hard to gauge demand for this feature.

What this would do is allow you to record parts of your current view model’s state that would be stored somewhere in ApplicationData.Current.LocalSettings for when the application is resumed. This would most likely be wired into the current methods on Navigation Service.

Get in touch

I’ve created an issue on GitHub to facilitate discussion around some of these issues and would invite feedback. Let us know what you think.

Application State, part 2

In the previous post on application state we defined a whole of terms on how we can consider state and some of the user expectations how how our applications should respect that state. This this post I want to discuss what’s already in Caliburn.Micro for these problems in particular for the Windows Phone (Silverlight) platform.

Phone Application Frame

Applications in Windows Phone (Silverlight) are primarily view first, the root content of the app’s Window is a PhoneApplicationFrame that Caliburn.Micro wraps in an INavigationService. As the app navigates to new PhoneApplicationPage’s our navigation service locates the appropriate view model for the view and activates it.

The PhoneApplicationFrame maintains an internal cache of the views that lines up with the back and forward stacks. This means as you navigate back you’re returning the exact same view that you left earlier.

This behaviour gives us our desired outcomes for View State out of the box as that state is automatically preserved on the forward and back stacks. You can see this behaviour in existing apps by scrolling down a list, navigating forward and then back. All View State such as list scroll position should be maintained correctly.

The INavigationService implementation FrameAdapter takes care not to overwrite existing view models. Because views retain a reference to their view model (through DataContext) then these cached views act as a cache for the view model as well. Navigation back to a cached view causes an activation of the existing view model rather than creation.

In summary the view caching behaviour of PhoneApplicationFrame coupled with Caliburn.Micro’s INavigationService give us the desired behaviour right out of the box.

App Activation

App activation and deactivation has changed a bit over the course of Windows Phone versions with the introduction of fast app resume, but the basics remain the same.

Caliburn.Micro has some built in features to let you preserve some parts of a view model during the tombstoning of an application through the StorageHandler<T> class. This implements the desired behaviour from part 1, you can read more about it in the documentation.

Where to from here?

In the next post I’ll discuss how we can go about addressing these same behaviours in the Windows RT platform.

2.0.1 point release

We’ve just pushed 2.0.1 to nuget, it’s a small release mostly with bug fixes missing from 2.0.0.

What’s New?

  • Resolved compilation errors in the Caliburn.Micro.Start package due to the breaking changes in 2.0.0.
  • PropertyChangedBase supports DataContract serialization on appropriate platforms.
  • Windows Phone 8.1 now exposes an INavigationService.BackPressed to better let apps handle and potentially cancel hardware back button events.
  • Added a convention for Windows Phone 8.1 Pivot.
  • Resolved a bug where OnViewReady argument was null.
  • ActionMessage is more resilient to null values.
  • Resolved BindingScope.FindNamedDescendants crashed on DependencyObjects
  • Added View.IsInDesignMode.

Thanks to all who contributed fixes, logged bugs etc.

Application State, part 1

Application State and it’s management is an incredibly important part of building rich interactive applications. It’s also a broad topic that covers a lot of different scenarios and can mean different things to different people. The next major release of Caliburn.Micro will include a number of things to help address this in WinRT (Windows 8 and Windows Phone 8.1) and I want to start framing the conversation.

I’ll start with defining the different sorts of state, outline the solutions on existing platforms such as Windows Phone Silverlight, then talks about the differences in WinRT.

What is state?

First I want to break state up into three broad buckets. There is some overlap between these buckets thanks to bindings but they’re generally discrete. These buckets are:

  1. View State
  2. View Model State
  3. Frame State

View State

View state are the states of the controls that make up the view, this would be things such as scroll position, selected items in list boxes, partially entered text etc.

View Model State

This is often what developers think of as application state, this is the current state of the view model and includes child view models loaded from external services. There’s an overlap with View State here with what’s being bound between view and view model. When this occurs I include it in View Model state.

Frame State

Almost all Windows Phone and most Windows applications are view first in that the root window content is a Frame that is wrapped in a navigation service. What I refer to as Frame state is the back and forward stacks of that Frame control.

When should we consider state?

I believe there’s two major times we need to consider the state of the application. They are:

  1. Frame navigation
  2. Application Suspension / Resumption

Frame Navigation

Users expect that when that when they navigate back to a view within an application it will be in the same state it was when they navigated away. Often this can be a subtle thing but will frustrate the user ALOT if they’re navigating back and forth between views frequently and it’s state is not retained. View model state will also be something the user will expect to be retained including things such as partially entered text fields and data loaded from web services.

Application / Suspension

In the more mobile xaml frameworks such as Windows Phone Silverlight and WinRT there are systems in place that in order to conserve resources an suspended application in the background may be terminated. When the user relaunches the application there will be an expectation that it resumes to the state they left the application.

How does this come together?

Obviously there is a relationship between the different sorts of state and when we should consider them, almost all of it is driven by user expectation. This can be different for individual applications (banking software can for security reasons have a different idea about resuming).

Below is my general thoughts on user expectations and how they relate to the two above lists.

  Frame Navigation Suspension / Resumption
View State Views from back and forward stacks should retain state. Views are generally not expected to retain state.
View Model State View models corresponding to the back and forward stacks should retain state. The view model for the “current” view would retain some of its state.
Frame State N/A The back and forward stacks of the Frame are retained.

Where to from here?

This post has been a lot of definitions to probably highlight what’s usually obvious but defining the terms for later discussion helps quite a bit.

In the next post I’ll outline how Caliburn.Micro deals with most of these problems on Windows Phone Silverlight.

Caliburn.Micro 2.0.0

Announcing a new version of Caliburn.Micro with support for all Xaml platforms, PCL and Universal app support, an improved web site and docs and new leadership for the future.

The Release

I’m elated to announce that today we are officially releasing Caliburn.Micro 2.0. A lot of work has gone into this release, including some major re-design to enable PCL and Universal App support. We’ve taken this opportunity to fix up the core of the framework so that it’s better designed to function in a more friction-free way across all Xaml platforms. Naturally, we’ve also fixed a ton of bugs and added some new features along the way. You can get it now on Nuget. We hope you ejoy the new version and that Caliburn.Micro continues to make your Xaml app development less like work and more like play :)

There are some breaking changes that comes with such a major redesign. Most of these have been documented at Migrating to 2.0.0

The Web Site

Correspondent with this release, we’re launching a new web site: caliburnmicro.com. The new site will function as a source of news, documentation and releases. We hope that giving the project a more official home and providing the docs in a cleaner, easy-to-read format will help you and future developers pick up and learn the library faster.

The Leadership

Thanks for this incredible work go entirely to Nigel Sampson and Thomas Ibel. I had nothing to do with this release except to answer an occasional question. That brings me to an important point. I’m not working with Xaml much anymore and haven’t personally been using Caliburn.Micro for development for a while now. So, I feel it’s time to pass the torch. As part of this release, we’re officially announcing that Nigel Sampson will be taking over the role of coordinating the Caliburn.Micro project. He’s worked with and helped develop Caliburn.Micro for a long time and he’s committed to its future and to open source. He’s a great man for the job and I’m happy to pass along my work to someone who shares the same vision for Xaml development. I’ll be staying on as an advisor, of course, but I expect things will get along quite well with Nigel in charge and Thomas there to keep refining the core.