I’ve been a bit proponent of both Xamarin and Xamarin.Forms for a long time but recently I’ve been taking another look at some of the fundamentals. Xamarin.Forms, for all its great intent is still a poor-man’s answer to declaring layout of an application in Xaml. Unlike Xaml platforms that have come before it (WPF, Silverlight, UWP), Forms doesn’t support some of even the basic features that you’d expect. On the chopping board today is the Button control – as one of the fundamental controls for any application, the Button should be able to be completely customised. Unfortunately it’s far from easy to alter the look of the basic button.
If you look at how a simple button renders on different platforms, there is a clear difference between the style of the button. The following image shows how the button control renders across UWP, iOS and Android (UWP has an extra visual state as it supports mouse/pointer over on desktop).
Button States Across Three Platforms
The difference in style is by design – the rendering of the button on each platform uses the built in style for each platform. This works well if you’re interesting in building a generic looking application (eg perhaps a simple LOB data capture application). However, if you want to style the button in line with your branding and the design of your application, you’re going to want to make the buttons look and behave the same across all devices. You’d think this would be as simple as defining a common style, or perhaps overriding the default layout of the button. This is in fact doable – for example on UWP you can use an implicit style to override the look of every button across the application.
Having to override the style of the button on each platform defeats the purpose of using Forms in the first place – once you’ve overridden the style, you then need to make platform specific changes every time you want to alter the style of the button control; imagine doing this for every control in your application – pretty soon, you might as well have developed the application using the traditional Xamarin model, rather than Forms. So, let’s look at the limitations of the Button control and see what we can do to address some of its shortcomings in a way that will help us build better applications quicker.
There are two issues that I have with the Button control. The first is that it’s not easy to customise the content within the Button itself. For example if you wanted to have two lines of text, with the first being centred in bold, and the second justified italics with word wrapping enabled to allow it to flow to multiple lines. In UWP this would be easy with the Button control as the Content could be customised to have multiple TextBlock elements, with the style set accordingly.
<Button>
<StackPanel>
<TextBlock Text=”Hello World!”
TextAlignment=”Center”
FontWeight=”Bold” />
<TextBlock Text=”This is a short message that should word wrap with all the lines being set to justified”
FontStyle=”Italic”
TextWrapping=”WrapWholeWords”
TextAlignment=”Justify” />
</StackPanel>
</Button>
This would look similar to the following:
UWP Button with Custom Content
Attempting to do this with the Forms Button control is not trivial. The second issue is how the different visual states are handled. Again in UWP the different Button visual states are defined in XAML (generic.xaml) where appropriate properties are set based on the active states (eg Normal, Pressed, PointerOver). Forms doesn’t support Visual States, so attempting to define Button states declaratively is going to take more work.
My solution to these issues comes in two steps: firstly, provide a declarative way to define Visual States in XAML, similar to what’s available in UWP; and secondly to provide an alternative to the Button which can not only take custom content but also has a template that can be override to customise behaviour for different button states. If you’ve ever drilled into a control template for a UWP control, chances are that it has some visual states defined. Rather than having some predefined behaviour that’s not accessible or customisable, the various states of UWP controls are defined in XAML in the template for the control. In addition to defining the different states within a control, visual states can be used to define different states of a page too.
In this post we’ll focus on the first step, which is how to define in XAML and then apply visual states. Visual states are defined in visual state groups, and only a single state within a given states group can be active at any time (ie they’re mutually exclusive). However, it is possible that two states, from different groups can be active at any point in time.
So far we know we need visual states, defined within visual state groups. Within each visual state we need to be able to define which properties are going to change when the visual state is made active. UWP allows the property changes to be defined either as a storyboard, potentially with animation, or as a series of setters where the property is updated to the new value immediately. For the purposes of getting the basics working, we’re just going to work with property setters.
Let’s work with a very simple scenario where we have a simple Label which we need to be able to show and hide based on the state of the page:
<Label FontSize=”30″
x_Name=”HelloLabel”
Text=”Hello World!” />
In our very contrived example we have a button that will be used to toggle whether the Label is visible.
private bool visible = true;
public void ToggleButtonPressed(object sender, EventArgs e)
{
visible = !visible;
VisualStateManager.GoToState(this, visible ? “Show”:”Hide”);
}
This code, by design, is similar to what you might see within a UWP application that uses visual states to control the layout – in this case showing and hiding an element. Currently visual states don’t exist in Xamarin Forms but here’s a first pass on an implementation of visual states:
<vsm:VisualStateManager.VisualStateGroups>
<x:Array Type=”{x:Type vsm:VisualStateGroup}”>
<vsm:VisualStateGroup Name=”LabelStates”>
<vsm:VisualState Name=”Show” />
<vsm:VisualState Name=”Hide”>
<vsm:VisualState.Setters>
<vsm:Setter Value=”false”
Target=”HelloLabel.IsVisible” />
</vsm:VisualState.Setters>
</vsm:VisualState>
</vsm:VisualStateGroup>
</x:Array>
</vsm:VisualStateManager.VisualStateGroups>
For those familiar with the syntax in UWP this should look familiar – there’s a few kinks to work out (for example the use of the Array tag) but in its initial form it works.
Sample project can be downloaded here