Начиная с версии 3.0 в Xamarin Forms была добавлена такая функциональность, как Visual State Manager - менеджер визуальных состояний.
По умолчанию Visual State Manager прикрепляет к элементам управления группу из трех состояний:
Disabled: элемент отключен для использования
Focused: элемент получил фокус и используется в текущий момент
Normal: стандартное состояние элемента
Visual State Manager поддерживается для объектов всех классов, которые унаследованы от VisualElement (в том числе классов View и Page). Итак, применим визуальные состояния к текстовому полю ввода:
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="HelloApp.MainPage"> <StackLayout> <Entry x:Name="entry"> <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="CommonStates"> <VisualState x:Name="Normal"> <VisualState.Setters> <Setter Property="FontSize" Value="25" /> </VisualState.Setters> </VisualState> <VisualState x:Name="Focused"> <VisualState.Setters> <Setter Property="FontSize" Value="35" /> <Setter Property="TextColor" Value="Green" /> </VisualState.Setters> </VisualState> <VisualState x:Name="Disabled"> <VisualState.Setters> <Setter Property="BackgroundColor" Value="LightGray" /> </VisualState.Setters> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> </Entry> <Button Text="Toggle" Clicked="Button_Clicked" /> </StackLayout> </ContentPage>
Визуальные состояния определяются с помощью прикрепляемого свойства VisualStateManager.VisualStateGroups. Оно имеет
тип VisualStateGroupList
и представляет коллекцию объектов VisualStateGroup, то есть группу визуальных
состояний. В данном случае добавлена только одна группа состояний, которая называется CommonStates:
<VisualStateGroup Name="CommonStates">
Каждая группа VisualStateGroup определяет набор состояний. Каждое состояние определяется с помощью объекта VisualState. Этот объект принимает ряд объектов Setter, с помощью которых устанавливаются значения свойств визуального элемента, когда он принимает данное состояние. То есть в данном случае, при вводе шрифт получает высоту в 35 единиц и зеленый цвет. Когда текстовое поле не имеет фокуса, но доступно для использования, применяется цвет шрифта по умолчанию и высота шрифта в 25 единиц. И когда текстовое поле недоступно для ввода, оно окрашивается в светло-серый цвет.
Стоит учитывать, что имена состояний в данном случае должны быть именно "Normal", "Disabled", "Focused", так как это встроенные по умолчанию состояния. Название группы необязательно должно быть "CommonStates", оно может быть произвольным.
Для переключения доступности поля Entry в данном примере определена кнопка, у которой в файле кода C# установлен обработчик нажатия Button_Clicked:
private void Button_Clicked(object sender, EventArgs e) { entry.IsEnabled = !entry.IsEnabled; }
По умолчанию Visual State Manager предоставляет нам три встроенных состояния: Normal, Focused и Disabled, однако мы можем определять
свои состояния. Суть встроенных состояний состоит в том, что элемент отлеживает некоторое изменения. Например, он отслеживает получение фокуса и после этого
вызывает метод VisualStateManager.GoToState()
, в который передается название состояния:
VisualStateManager.GoToState(visualElement, "Focused");
В качестве первого параметра передается элемент, для которого вызываюся состояния. Это должен быть элемент типа, который унаследован от VisualElement.
Соответственно при создании своих состояний мы должны сами отслеживать переход в эти состояния и вызывать данный метод.
Например, определим визуальные состояния для валидации данных в текстовом поле. Для этого определим следующую страницу:
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="HelloApp.MainPage"> <StackLayout> <Entry x:Name="myEntry" TextChanged="entry_TextChanged"> <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="ValidStates"> <VisualState x:Name="Valid"> <VisualState.Setters> <Setter Property="FontSize" Value="25" /> <Setter Property="TextColor" Value="Green" /> </VisualState.Setters> </VisualState> <VisualState x:Name="Invalid"> <VisualState.Setters> <Setter Property="FontSize" Value="35" /> <Setter Property="TextColor" Value="Red" /> </VisualState.Setters> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> </Entry> </StackLayout> </ContentPage>
Здесь к полю Entry прикреплена групаа состояний ValidStates (название группы может быть произвольным). Группа содержит два визуальных состояния: "Valid" и "Invalid". Каждое из состояний предусматривает определенные цвет и высоту шрифта.
Но по умолчанию Entry не отслеживает подобные состояния, и нам надо самостоятельно вызывать их. Для этого изменим код c# у страницы:
using System.Text.RegularExpressions; using Xamarin.Forms; namespace HelloApp { public partial class MainPage : ContentPage { public MainPage() { InitializeComponent(); SetState(false); // изначально невалидное состояние } private void entry_TextChanged(object sender, TextChangedEventArgs e) { // проверяем введенное значение на соответствие регулярному выражению bool isValid = Regex.IsMatch(e.NewTextValue, @"^\+[1-9]-\d{3}-\d{3}-\d{4}$"); SetState(isValid); } // установка состояния Valid или Invalid void SetState(bool isValid) { string visualState = isValid ? "Valid" : "Invalid"; VisualStateManager.GoToState(myEntry, visualState); } } }
Допустим, нам надо вввести в текстовое поле номер телефона в формате +1-234-567-8901. Для этого прикрепляем к текстовому полю обработчик изменения ввода entry_TextChanged, в котоом проверяем новый введенный текст. Если он соответствует регулярному выражению, то вызывается метод
VisualStateManager.GoToState(myEntry, "Valid");
То есть для элемента myEntry вызывается событие Valid, иначе вызывается событие Invalid. И для упрощения вызов данного метода инкапсулирован в методе SetState.
Поскольку при запуске приложения текстовое поле уже должно быть в определенном состоянии, то в конструкторе также вызывается метод VisualStateManager.GoToState.
Результат работы приложения:
При прикреплении группы визуальных состояний к элементам код элемента может сильно разбухать. Хотя визуальные состояния не относятся к структуре элемента, а только к его стилизации. Кроме того, возможно, мы захотим использовать одни и те же визуальные состояния для разных элементов. В этом случае удобнее выносить визуальные состояния в ресурсы. Например, изменим выше определение страницы 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" x:Class="HelloApp.MainPage"> <ContentPage.Resources> <Style TargetType="Entry" x:Key="ValidStatesStyle"> <Setter Property="VisualStateManager.VisualStateGroups"> <VisualStateGroupList> <VisualStateGroup x:Name="ValidStates"> <VisualState x:Name="Valid"> <VisualState.Setters> <Setter Property="FontSize" Value="25" /> <Setter Property="TextColor" Value="Green" /> </VisualState.Setters> </VisualState> <VisualState x:Name="Invalid"> <VisualState.Setters> <Setter Property="FontSize" Value="35" /> <Setter Property="TextColor" Value="Red" /> </VisualState.Setters> </VisualState> </VisualStateGroup> </VisualStateGroupList> </Setter> </Style> </ContentPage.Resources> <StackLayout> <Entry x:Name="entry" TextChanged="entry_TextChanged" Style="{StaticResource Key=ValidStatesStyle}" /> </StackLayout> </ContentPage>