Having worked with each of the different XAML based technologies Microsoft has released (WPF, SL, WP, Win, UWP…), I’ve long been a proponent of the use of visual states. Jerry Nixon discusses using visual states to build Windows 8.1 application. I’ve written articles that talk about using visual states for changing page layout to reflect states within a view model. Let me talk briefly about why I favour visual states and why the visibility property is often misused…. then we’ll get onto discussing using states for building applications.
Developers new to working with XAML discover that they can hide/show items on the page by data binding a Boolean property on the view model (this assumes they’re using mvvm/data binding) with the visibility property on an element. This of course requires a Boolean to visibility converter. It works nicely and assuming the developer has implemented INotifyPropertyChanged and is raising the PropertyChanged event correctly, the elements on the page will hide/show as required.
Whilst there isn’t anything wrong with this approach, it makes it difficult to design the layout in Blend. In order to hide/show elements, you have to modify the design time data (assuming you’ve bothered to create design time data). The alternative is to use visual states to describe how the page looks for each state. Blend for Visual Studio includes a States window which allows for one or more states to be invoked, making it easy to create, view and edit visual states, at design time!
Ok, time to move on an talk more about using states as a foundation for application development. Unfortunately we can’t jump there yet as we need to first look at different levels of how an application hangs together. Rather than get into a discussion on the similarities and differences between application platforms, let’s just look at the Windows platform (this should apply to Windows Phone, Windows 8.x and Windows 10):
– An application typically has a frame, within which it navigates between pages
– A page can be made up of any number of visual state groups, and can have a single visual state selected at any time from each visual state group. Animations can be run as part of transitioning between states.
– Controls (including Usercontrols) can contain their own visual states that determine how they look under various conditions (pressed, checked, selected etc).
What’s interesting about this is that the navigation between pages is actually the odd one out. Most application developers devote a large portion of the application development process to ensuring the navigation between pages is correct. They spend very little time working through the visual states of pages (often hacking away at hiding/showing elements on the screen). So, what happens if we make the assumption that each one of these scenarios can be expressed as a series of states?
As a starting point, we’d need a definition of a state. In the past, to keep the states of a view model separated from the visual states on a page I typically create an enumeration which has values that have the same names as the visual states. The view model then keeps track of which state is currently selected. Changing the current state raised a StateChanged event, which is handled by the page where it invokes the GoToState method on the VisualStateManager. My basic definition of a state will be an extension of this where the IStateDefinition is based on an enumeration type (well, at least a struct):
public interface IStateDefinition<TState> where TState : struct
{
TState State { get; }
}
with a simple implementation:
public class BaseStateDefinition<TState> : IStateDefinition<TState>
where TState : struct
{
public TState State { get; set; }
}
And we can then define an interface for an entity which maintains current state, allows for a change in state and raise an event when the state changes:
public interface IStateManager<TState> : INotifyPropertyChanged
where TState : struct
{
event EventHandler<StateEventArgs<TState>> StateChanged;TState CurrentState { get; }
IDictionary<TState, IStateDefinition<TState>> States { get; }
Task<bool> ChangeTo(TState newState, bool useTransition = true);
}
The first instinct would be for the view model to implement this interface, but this won’t work since the view model would have to implement the interface for each state group that it needs to track. This means we need an implementation of this interface that tracks current state for each state group:
public class BaseStateManager<TState> :
NotifyBase, IStateManager<TState>
where TState : struct
{
public event EventHandler<StateEventArgs<TState>> StateChanged;public TState CurrentState { get; private set; }
public IDictionary<TState, IStateDefinition<TState>> States { get; set; }
public async Task<bool> ChangeTo(TState newState, bool useTransitions = true)
{
var current = CurrentState;
if (current.Equals(newState)) return true;CurrentState = newState;
try
{
StateChanged?.Invoke(this, new StateEventArgs<TState>(newState, useTransitions));
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
// Ignore any errors caused by the event being raised, as
// the state change has still occurred
}
return true;
}
}
We’ll use this as the basis from which we can track states at an application, page and control level.