Visual states
Visual states and VSM will be familiar concepts for WPF and UWP developers; however, they were missing from Xamarin.Forms' runtime until recently. Visual states define various conditions that a control can be rendered according to certain conditions. For instance, an Entry element can be in Normal, Focused, or Disabled states and each state defines a different visual setter for the element. Additionally, custom states can also be defined for a visual element and, depending on triggers or explicit calls to VisualStateManager, can manage the visual state of elements.
In order to demonstrate this, we can create three different states for our label (for example, Released, UnReleased, and Unknown) and deal with states using our triggers.
First, we need to define states for our label control (which can then be moved to a resource dictionary as part of a style):
<Label x:Name="ReleaseDate" ...>
<Label.Triggers>
<!-- Removed for Brevity -->
</Label.Triggers>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Released">
<VisualState.Setters>
<Setter
Property="BackgroundColor"
Value="Lime" />
<Setter
Property="TextColor"
Value="Black" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="UnReleased">
<VisualState.Setters>
<Setter Property="TextColor" Value="Black" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Unknown">
<VisualState.Setters>
<Setter Property="TextColor" Value="Red" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Label>
As you can see, one of the defined states is Unknown, and it should set the text color to red. In order to change the state of the label using a trigger, we can implement a trigger action:
public class ChangeStateAction : TriggerAction<VisualElement>
{
public ChangeStateAction() { }
public string State { set; get; }
protected override void Invoke(VisualElement visual)
{
if(visual.HasVisualStateGroups())
{
VisualStateManager.GoToState(visual, State);
}
}
}
And we can use this action as our EnterAction for the previously defined multi-trigger:
<MultiTrigger TargetType="Label">
<MultiTrigger.Conditions>
<!-- Removed for brevity -->
</MultiTrigger.Conditions>
<MultiTrigger.EnterActions>
<actions:ChangeStateAction State="Unknown" />
</MultiTrigger.EnterActions>
</MultiTrigger>
We can achieve the same result as using setters. However, it is important to mention that, without defining an ExitAction once the label is set to the given state, it will not revert to the previous state.