Skip to main content Skip to footer

Tips for Building Adaptive Apps with Xamarin.Forms - Part 3

Xamarin has always provided tools for building adaptive UIs, but with the release of Xamarin.Forms 3.0 they have added some new tools for developers. The Visual State Manager (VSM) is a familiar concept for most XAML developers (while being relatively new to Xamarin), and it allows you to make changes to XAML elements based on visual states that are set in code. Recently, Xamarin has added FlexLayout container to augment the other layout containers available (with some notable improvements over StackLayout), and it adds many new properties that are useful for adaptive layouts.

In two previous articles, we discuss several tips for building adaptive apps with Xamarin.Forms and adaptive techniques with content views and master detail.

In this blog we’ll use new tools to further develop adaptive Xamarin.Forms apps.

Download Now!<%/if%>

Visual State Manager

The Visual State Manager provides a structure for making visual changes to a user control based on it’s underlying state. The state of a control (for example, a Button) could be enabled, disabled, pressed, or focused. These states are collected into groups that are mutually exclusive and can easily be identified. The state Normal, Disabled, and Focused are available for View, Page, and any other class that derives from VisualElement.

You can also define your own visual state groups and visual states. A classic example for using the VSM is input validation, where an input control clearly exists in different states depending on what’s been entered. The control could have valid or invalid text, and it makes sense to provide some feedback to a user to illustrate the present state.

We’ll examine a simple sample that validates based on the value length of text in a C1MaskedEntry control. In the XAML, we can define the C1MaskedEntry control (set up to handle a date) and define visual states for “InProgress” and “Finished.” These two states will be very simple. The validation will check the length of the text entered into the control to make sure it’s long enough in the C# code behind.

Depending on the result of the validation check, we’ll use the GoToState method to change state.

XAML

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:VSM"
             xmlns:c1="clr-namespace:C1.Xamarin.Forms.Input;assembly=C1.Xamarin.Forms.Input"
             x:Class="VSM.MainPage"
             xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core" 
             ios:Page.UseSafeArea="true">
    <StackLayout>
        <c1:C1MaskedEntry x:Name="MaskedEntry" HeightRequest="50" Placeholder="Enter Date" Mask="90/90/0000" TextChanged="OnTextChanged">
            <VisualStateManager.VisualStateGroups>
                <VisualStateGroup Name="ValidationStates">
                    <VisualState Name="Finished" >
                        <VisualState.Setters>
                            <Setter Property="BackgroundColor" Value="LightGreen" />
                        </VisualState.Setters>
                    </VisualState>
                    <VisualState Name="InProgress">
                        <VisualState.Setters>
                            <Setter Property="BackgroundColor" Value="LightYellow" />
                        </VisualState.Setters>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>
        </c1:C1MaskedEntry>
    </StackLayout>
</ContentPage>

C#

using Xamarin.Forms;

namespace VSM
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
        }
        void OnTextChanged(object sender, TextChangedEventArgs args)
        {
           bool isFinished = MaskedEntry.Value.Length == 8 ? true : false;

           GoToState(isFinished);
        }
        void GoToState(bool isFinished)
        {
            string visualState = isFinished ? "Finished" : "InProgress";
            VisualStateManager.GoToState(MaskedEntry, visualState);
        }
    }
}

Tips for Building Adaptive apps with Xamarin.Forms

More layers of validation could be added here along with more visual states. While that sample shows the basic idea of how the Visual State Manager works, you may wonder how the VSM applies to making adaptive apps. Since the VSM applies singularly to an application’s visuals, it provides a direct way of dealing with adaptive layout. Details like orientation changes, platform differences, and screen size changes can be handled using the VSM. We’ll revisit the concept of a mobile dashboard on iOS.

When a device’s orientation changes, we may want to display information in a slightly different layout so that it makes the best use of screen space. Rather than allowing a Stacklayout container to stretch the content, we can create a new layout that better displays the information.

Tips for Building Adaptive apps with Xamarin.Forms

Instead, we can use a combination of multiple Stacklayouts with the VSM to achieve something like this:

Tips for Building Adaptive apps with Xamarin.Forms

This is relatively easy to accomplish using the VSM. All we need to do is create an orientation state in XAML and tie it to the Orientation property of the parent StackLayout. In our code behind we can then check whether the device is in Portrait or Landscape in the SizeChanged event and return the corresponding state.

XAML

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:gauge="clr-namespace:C1.Xamarin.Forms.Gauge;assembly=C1.Xamarin.Forms.Gauge"
             xmlns:chart="clr-namespace:C1.Xamarin.Forms.Chart;assembly=C1.Xamarin.Forms.Chart"
             xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core" 
             ios:Page.UseSafeArea="true"
             Padding="10"
             x:Class="VSM.VSMOrientation">
    <StackLayout x:Name="Stack">
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup Name=" OrientationStates">
                <VisualState Name="Landscape" >
                    <VisualState.Setters>
                        <Setter Property="Orientation" Value="Horizontal" />
                    </VisualState.Setters>
                </VisualState>
                <VisualState Name="Portrait">
                    <VisualState.Setters>
                        <Setter Property="Orientation" Value="Vertical" />
                    </VisualState.Setters>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
        <chart:FlexChart x:Name="chart" Grid.Row="0" VerticalOptions="FillAndExpand" ItemsSource="{Binding Data}" BindingX="Name" ChartType="Column">
            <chart:FlexChart.Series>
                <chart:ChartSeries x:Name="Sales2014" SeriesName="Sales" Binding="Sales"/>
            </chart:FlexChart.Series>
        </chart:FlexChart>
        <StackLayout VerticalOptions="FillAndExpand">
            <chart:FlexPie x:Name="pie" VerticalOptions="FillAndExpand" ItemsSource="{Binding Data}" BindingName="Name" Binding ="Sales" InnerRadius="0.5" LegendPosition="Bottom" LegendOrientation="Horizontal"/>
            <gauge:C1BulletGraph x:Name="graph3" ShowText="All" Value=".73"  Format="P0" Min="0" Max="1" Thickness="0.75" Good="1" Bad=".50" Target=".70"/>
        </StackLayout>
    </StackLayout>
</ContentPage>

C#

using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace VSM
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class VSMOrientation : ContentPage
    {
        private DataSource ds;
        public VSMOrientation()
        {
            InitializeComponent();
            ds = new DataSource();
            this.chart.BindingContext = ds;
            this.pie.BindingContext = ds;
            SizeChanged += (sender, args) =>
            {
                string visualState = Width > Height ? "Landscape" : "Portrait";
                VisualStateManager.GoToState(Stack, visualState);
            };
        }
    }

}

Obviously, scenarios can become quite a bit more complex than what we’ve presented, but the VSM provides an easy and flexible mechanism for handling for styling and adaptive layouts. Xamarin documentation provides even more samples that cover many other scenarios.

A new layout container for Xamarin.Forms 3.0

FlexLayout is a new layout container that Xamarin introduced with Xamarin.Forms 3.0. It acts like a successor to the StackLayout container with some new powerful tricks. While like the StackLayout in concept, it provides many more options for wrapping children, ordering them, justification, alignment, and orientation. It’s tuned towards designing adaptive layouts in Xamarin.Forms. It brings new options for laying out your UI, spacing, and positioning fluidly across multiple device sizes.

FlexLayout can lay its children out along a horizontal row or vertical column. This is controlled by setting its Direction property (similiar to the Orientation property in StackLayouts). However, it has more advanced tricks, such as RowReverse and ColumnReverse (that flips the order of its children). You can easily specify the alignment of items and content justification so a page of sparse control usage occupies the space intelligently (another advantage here over the StackLayout).

FlexLayout provides further options for positioning its children within the layout including Wrap, AlignItems, and JustifyContent properties. Wrapping makes it easy to overflow items rather than keep them all positioned in a single row or column. The children can also be wrapped in the reverse direction, meaning that they’ll wrap in the opposite direction of the normal wrap direction for their local. This behavior is optional as well, and you can always set Wrap = “NoWrap” if wrapping is undesirable.

AlignItems, on the other hand, dictate how children will be laid out within their rows or columns. You can set this to align to the Start, Center, End, or Stretch (which stretches the child elements from start to end).

Finally, you can set the JustifyContent property to control how the child elements are justified when there is extra space around them. There are options for Center, End, SpaceAround, SpaceBetween, SpaceEvenly, and Start. To demonstrate how these properties work, we’ll look at a simple example that uses BoxViews of various colors to illustrate how they’ll adapt to different layout changes using these properties.

Since this is relatively simple, we’ll just give the XAML here:

        <FlexLayout>
            <BoxView BackgroundColor="Red" HeightRequest="100" WidthRequest="100"/>
            <BoxView BackgroundColor="Blue" HeightRequest="100" WidthRequest="100"/>
            <BoxView BackgroundColor="Green" HeightRequest="100" WidthRequest="100"/>
            <BoxView BackgroundColor="Yellow" HeightRequest="100" WidthRequest="100"/>
            <BoxView BackgroundColor="Orange" HeightRequest="100" WidthRequest="100"/>
            <BoxView BackgroundColor="Purple" HeightRequest="100" WidthRequest="100"/>
        </FlexLayout>

We’ll configure each property.

Tips for Building Adaptive apps with Xamarin.Forms

Tips for Building Adaptive apps with Xamarin.Forms

Tips for Building Adaptive apps with Xamarin.Forms

In a more complicated sample we can use these properties together with some Xamarin.Forms controls to arrange children within the layout of a simple page. The page will consist of a C1MaskedEntry, Label, and Button, and use the FlexLayout as a container so that these children are well positioned in space. We’ll specify the Direction as Column so that they’re the controls are laid out vertically, set AlignItems to Center so that they’re positioned in the middle of the horizontal axis, and set JustifyContent to SpaceEvenly so that they have an equal amount of white space between the controls.

XAML

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:VSM"
             xmlns:c1="clr-namespace:C1.Xamarin.Forms.Input;assembly=C1.Xamarin.Forms.Input"
             x:Class="VSM.MainPage"
             xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core" 
             ios:Page.UseSafeArea="true">
    <FlexLayout Direction="Column" AlignItems="Center" JustifyContent="SpaceEvenly">
        <c1:C1MaskedEntry x:Name="MaskedEntry" HeightRequest="50" Placeholder="Enter Date" Mask="90/90/0000" TextChanged="OnTextChanged"/>
        <Label Text="Working on Adaptive Layouts"/>
        <Button Text="Learning to Adapt"/>
    </FlexLayout>
</ContentPage>

Tips for Building Adaptive apps with Xamarin.Forms

FlexLayout also some more advanced behaviors available including several attached bindable properties. These control the ordering of children (the Order property), individual alignment of controls (AlignSelf), the amount of space that’s allotted to the children of a FlexLayout (Basis), and the ability to specify how children should behave when there is extra space to occupy or there is a lack of space available (Grow and Shrink respectively). These properties allow you to micromanage the way your Views behave when place inside a FlexLayout, and it allow you to further fine tune how a layout behaves. The control also supports CSS styling which may be popular for some users, especially those with a lot of past web experience, and it adds to the overall flexibility of the control (since you have the options of setting styles in XAML, CSS, or C#).

Xamarin’s documentation provides a wealth of different examples and images to illustrate the ways FlexLayout can be used to improve an app. It can easily take the place of a StackLayout while also providing more functionality which makes it easy to incorporate into an existing project.

Writing adaptive apps wrap-up

Writing adaptive apps using Xamarin.Forms is extremely important since it involves tackling three platforms at once (Android, iOS, and UWP) and spans across Mobile, Tablet, and Desktop. Both the Visual State Mananager and FlexLayout are important tools for making better apps that look and feel good on a variety of devices. Each tool is only available with Xamarin.Forms 3.0, but they’re strong reasons to upgrade your projects and check out what’s new.

Download Now!<%/if%>

Kelley Ricker

comments powered by Disqus