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

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

Visual State Manager (менеджер визуальных состояний) позволяет организовать изменения визуального интерфейса. По умолчанию Visual State Manager прикрепляет к элементам управления группу из трех состояний:

  • Disabled: элемент отключен для использования

  • Focused: элемент получил фокус и используется в текущий момент

  • Normal: стандартное состояние элемента

  • PointerOver: указатель мыши находится над элементом

Состояния Normal, Disabled, Focused и PointerOver поддерживается для объектов всех классов, которые унаследованы от VisualElement (в том числе классов View и Page). Кроме того, при необходимости можно определять свои визуальные состояния.

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

Итак, применим визуальные состояния к текстовому полю Entry:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
             x:Class="HelloApp.MainPage"> 
    <StackLayout>
        <Entry x:Name="entry" Text="Hello METANIT.COM!">
            <VisualStateManager.VisualStateGroups>
                <VisualStateGroup x:Name="CustomStates">

                    <VisualState x:Name="Focused">
                        <VisualState.Setters>
                            <Setter Property="TextColor" Value="#004D40" />
                            <Setter Property="BackgroundColor" Value="#B2DFDB" />
                        </VisualState.Setters>
                    </VisualState>

                    <VisualState x:Name="PointerOver">
                        <VisualState.Setters>
                            <Setter Property="TextColor" Value="#004D40" />
                            <Setter Property="FontAttributes" Value="Bold" />
                        </VisualState.Setters>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>
        </Entry>
    </StackLayout>
</ContentPage>

Визуальные состояния определяются с помощью прикрепляемого свойства VisualStateManager.VisualStateGroups. Оно имеет тип VisualStateGroupList и представляет коллекцию объектов VisualStateGroup, то есть группу визуальных состояний. В данном случае добавлена только одна группа состояний, которая называется CustomStates:

<VisualStateGroup Name="CustomStates">

Каждая группа VisualStateGroup определяет набор состояний. Каждое состояние определяется с помощью объекта VisualState. Этот объект принимает ряд объектов Setter, с помощью которых устанавливаются значения свойств визуального элемента, когда он принимает данное состояние. Например, когда поле Entry находится в состоянии "Focused" (то есть получило фокус), срабатывает следующее состояние:

<VisualState x:Name="Focused">
    <VisualState.Setters>
        <Setter Property="TextColor" Value="#004D40" />
        <Setter Property="BackgroundColor" Value="#B2DFDB" />
    </VisualState.Setters>
</VisualState>

То есть в данном случае, при получении фокуса текст окрашивается в темнозеленый цвет, а фон - в светлозеленый.

Когда поле Entry находится в состоянии "PointerOver" (то есть указатель мыши находится над элементом), срабатывает следующее состояние:

<VisualState x:Name="PointerOver">
    <VisualState.Setters>
        <Setter Property="TextColor" Value="#004D40" />
        <Setter Property="FontAttributes" Value="Bold" />
    </VisualState.Setters>
</VisualState>

Таким образом при наведении указателя мыши на элемент текст окрашивается в темнозеленый цвет и получает выделение жирным.

Стоит учитывать, что имена состояний в данном случае должны быть именно "Normal", "Disabled", "Focused", "PointerOver", так как это встроенные по умолчанию состояния. Название группы необязательно должно быть "CustomStates", оно может быть произвольным. Но следует учитывать, что и название состояний и название групп должны быть уникальными ( то есть нельзя определить для элемента Entry для состояния "Disabled" или две группы состояний "CustomStates").

Visual State Manager и визуальные состояния в приложении на .NET MAUI и C#

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

При прикреплении группы визуальных состояний к элементам код элемента может сильно разбухать. Хотя визуальные состояния не относятся к структуре элемента, а только к его стилизации. Кроме того, возможно, мы захотим использовать одни и те же визуальные состояния для разных элементов. В этом случае удобнее выносить визуальные состояния в ресурсы. Например, изменим выше определение страницы xaml:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
             x:Class="HelloApp.MainPage">
    <ContentPage.Resources>
        <Style TargetType="Entry">
            <Setter Property="VisualStateManager.VisualStateGroups">
                <VisualStateGroupList>
                    <VisualStateGroup x:Name="CustomStates">

                        <VisualState x:Name="Focused">
                            <VisualState.Setters>
                                <Setter Property="TextColor" Value="#004D40" />
                                <Setter Property="BackgroundColor" Value="#B2DFDB" />
                            </VisualState.Setters>
                        </VisualState>

                        <VisualState x:Name="PointerOver">
                            <VisualState.Setters>
                                <Setter Property="TextColor" Value="#004D40" />
                                <Setter Property="FontAttributes" Value="Bold" />
                            </VisualState.Setters>
                        </VisualState>
                    </VisualStateGroup>
                </VisualStateGroupList>
            </Setter>
        </Style>
    </ContentPage.Resources>
    <StackLayout Padding="20">
        <Entry Text="Hello METANIT.COM!" />
    </StackLayout>
</ContentPage>

Визуальные состояния по умолчанию

Проект .NET MAUI по умолчанию содержит стили для элемента Entry в файле Resources/Styles/Styles.xaml, в которых определены эти состояния:

Визуальные состояния элементов по умолчанию в .NET MAUI и C#

Но если мы определяем визуальные состояния через стили, то мы можем переопределить эти визуальные состояния по умолчанию:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
             x:Class="HelloApp.MainPage">
    <ContentPage.Resources>
        <Style TargetType="Entry">
            <Setter Property="Margin" Value="10" />
            <Setter Property="VisualStateManager.VisualStateGroups">
                <VisualStateGroupList>
                    <VisualStateGroup x:Name="CustomStates">
                        <VisualState x:Name="Normal">
                            <VisualState.Setters>
                                <Setter Property="TextColor" Value="#004D40" />
                            </VisualState.Setters>
                        </VisualState>

                        <VisualState x:Name="Focused">
                            <VisualState.Setters>
                                <Setter Property="TextColor" Value="#004D40" />
                                <Setter Property="BackgroundColor" Value="#B2DFDB" />
                            </VisualState.Setters>
                        </VisualState>

                        <VisualState x:Name="Disabled">
                            <VisualState.Setters>
                                <Setter Property="TextColor" Value="Gray" />
                                <Setter Property="BackgroundColor" Value="LightGray" />
                            </VisualState.Setters>
                        </VisualState>

                        <VisualState x:Name="PointerOver">
                            <VisualState.Setters>
                                <Setter Property="TextColor" Value="#004D40" />
                                <Setter Property="FontAttributes" Value="Bold" />
                            </VisualState.Setters>
                        </VisualState>
                    </VisualStateGroup>
                </VisualStateGroupList>
            </Setter>
        </Style>
    </ContentPage.Resources>
    <StackLayout Padding="10">
        <Entry Text="Hello METANIT.COM!" />
        <Entry Text="Hello World!" />
        <Entry Text="Hello Work!" IsEnabled="False" />
    </StackLayout>
</ContentPage>
Переопределение визуальных состояний элементов в .NET MAUI и C#

Доступные визуальные состояния

    В .NET MAUI для ряда элементов есть дополнительные состояния:

  • VisualElement определяет общие для всех элементов состояния Normal, Disabled, Focused и PointerOver

  • Button определяет состояние Pressed (нажатие кнопки)

  • CarouselView определяет состояния DefaultItem, CurrentItem, PreviousItem и NextItem

  • CheckBox определяет состояние IsChecked (флажок отмечен)

  • CollectionView определяет состояние Selected (элемент выделен)

  • ImageButton определяет состояние Pressed (нажатие кнопки)

  • RadioButton определяет состояния Checked (элемент выбран) и Unchecked (элемент не выбран)

  • Switch определяет состояния On (включено) и Off(выключено)

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

По умолчанию Visual State Manager предоставляет нам пять встроенных состояния: Normal, Focused, PointerOver и Disabled. Однако мы можем определять свои состояния. Суть встроенных состояний состоит в том, что элемент отлеживает некоторое изменения. Например, он отслеживает получение фокуса и после этого вызывает метод VisualStateManager.GoToState(), в который передается название состояния:

VisualStateManager.GoToState(visualElement, "Focused");

В качестве первого параметра передается элемент, для которого вызываюся состояния. Это должен быть элемент типа, который унаследован от VisualElement.

Соответственно при создании своих состояний мы должны сами отслеживать переход в эти состояния и вызывать данный метод.

Например, определим визуальные состояния для валидации данных в текстовом поле. Для этого определим следующую страницу:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
             x:Class="HelloApp.MainPage">
    <StackLayout Padding="20">
        <Entry x:Name="myEntry" TextChanged="entry_TextChanged">
            <VisualStateManager.VisualStateGroups>
                <VisualStateGroup x:Name="ValidStates">
                    <VisualState x:Name="Valid">
                        <VisualState.Setters>
                            <Setter Property="TextColor" Value="Green" />
                        </VisualState.Setters>
                    </VisualState>

                    <VisualState x:Name="Invalid">
                        <VisualState.Setters>
                            <Setter Property="TextColor" Value="Red" />
                        </VisualState.Setters>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>
        </Entry>
    </StackLayout>
</ContentPage>

Здесь к полю Entry прикреплена групаа состояний ValidStates (название группы может быть произвольным). Группа содержит два визуальных состояния: "Valid" и "Invalid". Каждое из состояний предусматривает определенные цвет и высоту шрифта.

Но по умолчанию Entry не отслеживает подобные состояния, и нам надо самостоятельно вызывать их. Для этого изменим код c# у страницы:

using System.Text.RegularExpressions;

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 в .NET MAUI и C#
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850