Visual State Manager и визуальные состояния

Последнее обновление: 12.01.2021

Начиная с версии 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 in Xamarin Forms

Определение своих визуальных состояний

По умолчанию 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.

Результат работы приложения:

Visual States in Xamarin

Визуальные состояния в ресурсах

При прикреплении группы визуальных состояний к элементам код элемента может сильно разбухать. Хотя визуальные состояния не относятся к структуре элемента, а только к его стилизации. Кроме того, возможно, мы захотим использовать одни и те же визуальные состояния для разных элементов. В этом случае удобнее выносить визуальные состояния в ресурсы. Например, изменим выше определение страницы 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>
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850