UWP: New features of Visual State Manager (part 2)

In the previous post I told about two new features of Visual State Manager like setters and adaptive triggers and you can see that adaptive triggers are limited to screen size only. But in real applications you can change your interface based on lots of different parameters like Xbox controller availability, screen orientation, data availability etc. So, in this article I want to show how to extend existing infrastructure of adaptive triggers creating new one.

It’s hard to find an application which is not connected to Internet. But getting data from the Internet takes some time. Usually I use ProgressRing to show that getting the data is in progress and I use VisualStateManager to show or hide elements based on data availability. Usually there are three states like Loading, Loaded and Error. Of course, I needed to implement some code which change state of application based on state of “view model”. Let’s see if it’s possible to implement our own trigger which helps to avoid coding in code-behind class of my page completely.

First of all we need to check if our “view model” class is ready for triggers. It’s better to implement a property which shows current state of our model as well as an event which fires every time when model change own state. Of course, it’s better to implement base class for all view models in our application.

 public enum StateEnum
{
 Loading,
 Loaded,
 Error
}
public class StateChangeEventArgs:EventArgs
{
 public StateEnum State { get; set; }
}
 
public delegate void StateChangedDelegate(object model, StateChangeEventArgs args);
 
public class PageViewModel
{
 public event StateChangedDelegate StateChanged;
 
 public void InitModel()
 {
 if (StateChanged != null) StateChanged.Invoke(this, new StateChangeEventArgs() { State = StateEnum.Loading });
 //load data
 if (StateChanged != null) StateChanged.Invoke(this, new StateChangeEventArgs() { State = StateEnum.Loaded });
 }
} 

I have implemented InitModel method as well as an example of code where we need to invoke StateChanged event. Usually you will implement this method in your own way and into inherited classes.

Once you have updated “view model” you can create object of it inside page XAML file:

 <Page.Resources>
 <st:PageViewModel x:Name="model"></st:PageViewModel>
</Page.Resources> 

It’s time to create your own trigger. In order to do it you need to create a new class which inherits StateTriggerBase class. Inside the class you can declare any method and properties but you need find a place where you will call SetActive method. Thanks to this method you can activate or deactivate your trigger. For example, I implemented the following class:

 public class DataTrigger: StateTriggerBase
{
 private PageViewModel model;
 
 public PageViewModel Model
 {
 get
 {
 return model;
 }
 set
 {
 model = value;
 model.StateChanged += Model_StateChanged;
 }
 }
 
 public string StateOfModel { get; set; }
 
 private void Model_StateChanged(object model, StateChangeEventArgs args)
 {
 SetActive(args.State.ToString().Equals(StateOfModel));
 }
}

You can see that I have two properties in the class which allow to set reference to the current view model and define a state which we are going to use to activate the trigger. Once view model is initialized we will activate or deactivate the trigger using the event handler for StateChanged event.

Finally, I declared the following states:

 <VisualState x:Name="Loading">
 <VisualState.Setters>
 <Setter Target="gridView.Visibility" Value="Collapsed"></Setter>
 <Setter Target="progress.Visibility" Value="Visible"></Setter>
 </VisualState.Setters>
 <VisualState.StateTriggers>
 <st:DataTrigger Model="{StaticResource model}" StateOfModel="Loading"></st:DataTrigger>
</VisualState.StateTriggers>
</VisualState>
<VisualState x:Name="Loaded">
 <VisualState.Setters>
 <Setter Target="gridView.Visibility" Value="Visible"></Setter>
 <Setter Target="progress.Visibility" Value="Collapsed"></Setter>
 </VisualState.Setters>
 <VisualState.StateTriggers>
 <st:DataTrigger Model="{StaticResource model}" StateOfModel="Loaded"></st:DataTrigger>
 </VisualState.StateTriggers>
</VisualState>

It’s really cool and allows to make better implementation of MVVM pattern.