Nick's .NET Travels

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

Getting Started with Visual States in Xamarin Forms using BuildIt.Forms

Over the past couple of months we’ve been working on getting the BuildIt.Forms library to a point where it was stable enough to promote and talk about. With the recent release of the 2.4 update for Xamarin Forms, we’re finally able to push out a stable release.

The BuildIt.Forms library brings with it quite a few features but probably the best reason to use the library is the support it adds for defining Visual States within your Xamarin Forms application within the XAML, just like you would if you were building using XAML for UWP (or any other XAML platform such as WPF etc). We’ve tried to stay as true as possible to the syntax for Visual States used on other platforms but like all things Xamarin Forms there are some differences that you’ll need to get your head around.

Ok, before we jump in, let’s take a back step and look at what a Visual State really is, and why you’d want to use them. Let’s start with a very simple example – imagine you have a page within your application that needs to load some data and you want to provide a visual cue that something is happening. In this case I’m going to add a simple overlay with some text indicating that information is being loaded:

<!-- Loading Prompt -->
<Grid BackgroundColor="LightGray"
         x:Name="LoadingIndicator"
         IsVisible="false">
     <Label Text="Loading..."
             HorizontalTextAlignment="Center"
             VerticalOptions="Center" />
</Grid>
<!-- END Loading Prompt –>

Note that the default IsVisible value is false, meaning that the LoadingIndicator Grid will not be visible. In the codebehind of the page I can then add code that would show the LoadingIndicator Grid, simulate loading content (ie Task.Delay), and then hide the LoadingIndicator Grid.

protected override async void OnAppearing()
{
     base.OnAppearing();

    LoadingIndicator.IsVisible = true;

    await Task.Delay(2000);

    LoadingIndicator.IsVisible = false;
}

Of course in a real application you wouldn’t do this, as there should be at least a ViewModel that would do the loading but we’ll come back to the use of ViewModels later. For the moment, it’s enough to point out that we controlling the appearance of individual elements based on the state of the page (ie showing the LoadingIndicator when the page is in the loading state). What would be great is if we could declare these states in XAML, which is where the BuildIt Forms library comes in.

I’ll add a few namespaces to the XAML for the page (mainly so I don’t forget to add them later when we talk about animations and other controls in the BuildIt Forms library):

xmlns:ctrl="clr-namespace:BuildIt.Forms.Controls;assembly=BuildIt.Forms.Controls"
xmlns:vsm="clr-namespace:BuildIt.Forms;assembly=BuildIt.Forms.Core"
xmlns:anim="clr-namespace:BuildIt.Forms.Animations;assembly=BuildIt.Forms.Core"

Next, immediately inside the root tag of the page, I’ll define two visual states:

<vsm:VisualStateManager.VisualStateGroups>
     <vsm:VisualStateGroups>
         <vsm:VisualStateGroup Name="LoadingStates">
             <vsm:VisualState Name="Loading">
                 <vsm:VisualState.Setters>
                     <vsm:Setter Element="{x:Reference LoadingIndicator}"
                                 Property="IsVisible"
                                 Value="true" />

                 </vsm:VisualState.Setters>
             </vsm:VisualState>
             <vsm:VisualState Name="Loaded" />
         </vsm:VisualStateGroup>
     </vsm:VisualStateGroups>
</vsm:VisualStateManager.VisualStateGroups>

The important things to note are the names of the states: Loading and Loaded, and the use of a Setter element to adjust the IsVisible property on the LoadingIndicator element. Note that I didn’t need to set the IsVisible property to false in the Loaded state – this is because the default value of the IsVisible property on the LoadingIndicator is set to false, and the Visual State manager is clever enough to remember that when switching between states.

Now that we have visual states defined, let’s switch to the code behind and adjust the code to use the visual states:

protected override async void OnAppearing()
{
     base.OnAppearing();
    await VisualStateManager.GoToState(this, "Loading");
    await Task.Delay(2000);
     await VisualStateManager.GoToState(this, "Loaded");
}

It looks like we haven’t made any great improvements but here’s where the power is – let’s add some content to the screen that will be shown when the loading is complete:

<!-- Login Prompt -->
<Grid x:Name="LoginPrompt"
         IsVisible="False">
     <StackLayout>
         <Label Text="Username:" />
         <Editor />
         <Label Text="Password:" />
         <Editor />
         <Button Text="Login" />
     </StackLayout>
</Grid>
<!-- END Login Prompt –>

In the past, this would have required more code in the code behind to adjust the IsVisible property on the LoginPrompt Grid in order to show the content. With visual states, there is no need to alter the logic of the application, I just need to adjust the Loaded visual state by adding a Setter to set the IsVisible property to true.

<vsm:VisualState Name="Loaded">
     <vsm:VisualState.Setters>
         <vsm:Setter Element="{x:Reference LoginPrompt}"
                     Property="IsVisible"
                     Value="true" />

     </vsm:VisualState.Setters>
</vsm:VisualState>

At the moment I have built an interface that provides useful feedback to the user about the loading state of the application. However, there is a rather abrupt change between the LoadingIndicator Grid and the LoginPrompt. It would be nice to be able to add animations to make the transition smoother. Again without adjusting the logic of the application I can do this by adding animations directly to the visual states. The following animation can be added after the Setters of the Loading state.

<vsm:VisualState.LeavingAnimations>
     <anim:AnimationGroup>
         <anim:AnimationGroup.PreAnimations>
             <anim:FadeAnimation Duration="500"
                                 Opacity="0"
                                 Element="{x:Reference LoadingIndicator}" />
         </anim:AnimationGroup.PreAnimations>
     </anim:AnimationGroup>
</vsm:VisualState.LeavingAnimations>

In this case the animation will be run prior to (ie a PreAnimation) any state change away from (ie a LeavingAnimation) the Loading state, and will fade the LoadingIndicator Grid out to 0 Opacity over a duration of 500 milliseconds.

In this post I’ve covered the basics of creating Visual States. In my next post I’ll cover using states in a ViewModel and how you can link them to the Visual States on the page to give you a more testable application.

Comments are closed