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

Диспетчер визуальных состояний .NET Multi-platform App Manager (.NET MAUI) предоставляет структурированный способ внесения визуальных изменений в пользовательский интерфейс из кода. В большинстве случаев пользовательский интерфейс приложения определен в XAML, и этот XAML может включать разметку, описывающую, как диспетчер визуальных состояний влияет на визуальные элементы пользовательского интерфейса.

Диспетчер визуальных состояний представляет концепцию визуальных состояний. Представление .NET MAUI, например представление Button может иметь несколько различных внешних представлений визуального элемента в зависимости от его базового состояния — отключается ли он или нажимается или имеет фокус ввода. Это состояния кнопки. Визуальные состояния собираются в группах визуальных состояний. Все визуальные состояния в группе визуальных состояний являются взаимоисключающими. Визуальные состояния и группы визуальных состояний определяются простыми текстовыми строками.

Диспетчер визуальных состояний .NET MAUI определяет группу визуальных состояний, именуемую CommonStates следующими визуальными состояниями:

  • Обычная
  • Выключено
  • Режим фокусировки
  • Выбрано
  • PointerOver

Состояния Normal, DisabledFocusedи PointerOver визуальные состояния поддерживаются во всех классах, производных от VisualElement, который является базовым классом для View и Page. Кроме того, можно также определить собственные группы визуальных состояний и визуальные состояния.

Преимущество использования визуального диспетчера состояний для определения внешнего вида, а не доступа к визуальным элементам непосредственно из кода за пределами кода, заключается в том, что визуальные элементы реагируют на другое состояние в XAML, который сохраняет весь дизайн пользовательского интерфейса в одном расположении.

Примечание.

Триггеры также могут вносить изменения в визуальные элементы в пользовательском интерфейсе на основе изменений в свойствах представления или срабатывании событий. Однако использование триггеров для работы с различными сочетаниями этих изменений может стать запутанным. С помощью диспетчера визуальных состояний визуальные состояния в группе визуальных состояний всегда являются взаимоисключающими. В любое время только одно состояние в каждой группе — текущее состояние.

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

Диспетчер визуальных состояний позволяет включить разметку в XAML-файл, который может изменить внешний вид представления, если представление нормальное, отключено, имеет фокус ввода, выбран или наведите курсор мыши на него, но не нажат. Они называются общими состояниями.

Например, предположим, что на странице есть Entry представление, и вы хотите, чтобы внешний вид визуального Entry элемента изменился следующим образом:

  • Должен Entry иметь розовый фон при Entry отключении.
  • Должен Entry иметь фон лайма обычно.
  • Должно Entry увеличиться до дважды его нормальной высоты, если он имеет фокус ввода.
  • У Entry него должен быть светло-синий фон, когда курсор мыши на него наведен, но не нажимается.

Вы можете присоединить разметку Visual State Manager к отдельному представлению или определить ее в стиле, если он применяется к нескольким представлениям.

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

Класс VisualStateManager определяет присоединенное VisualStateGroups свойство, которое используется для присоединения визуальных состояний к представлению. Свойство VisualStateGroups имеет тип VisualStateGroupList, который является коллекцией VisualStateGroup объектов. Таким образом, дочерний элемент присоединенного VisualStateManager.VisualStateGroups свойства является VisualStateGroup объектом. Этот объект определяет атрибут, указывающий x:Name имя группы. Кроме того, VisualStateGroup класс определяет Name свойство, которое можно использовать. Дополнительные сведения о присоединенных свойствах см. в разделе "Присоединенные свойства".

Класс VisualStateGroup определяет свойство с именем States, которое является коллекцией VisualState объектов. States — свойство содержимого VisualStateGroups класса, которое позволяет включать VisualState объекты в качестве дочерних VisualStateGroupобъектов. Каждый VisualState объект должен быть определен с помощью x:Name или Name.

Класс VisualState определяет свойство с именем Setters, которое является коллекцией Setter объектов. Это те же Setter объекты, которые используются в объекте Style . Setters не является свойством содержимого VisualState, поэтому необходимо включить теги элементов свойства для Setters свойства. Setter Объекты должны вставляться в виде дочерних Settersобъектов. Каждый Setter объект указывает значение свойства при текущем состоянии. Любое свойство, на которое Setter ссылается объект, должно поддерживаться привязываемым свойством.

Внимание

Чтобы объекты визуального Normal состояния Setter функционировали правильно, VisualStateGroup должен содержать VisualState объект для состояния. Если у этого визуального состояния нет Setter объектов, его следует включить в качестве пустого визуального состояния (<VisualState Name="Normal" />).

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

<Entry FontSize="18">
    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup Name="CommonStates">
            <VisualState Name="Normal">
                <VisualState.Setters>
                    <Setter Property="BackgroundColor" Value="Lime" />
                </VisualState.Setters>
            </VisualState>

            <VisualState Name="Focused">
                <VisualState.Setters>
                    <Setter Property="FontSize" Value="36" />
                </VisualState.Setters>
            </VisualState>

            <VisualState Name="Disabled">
                <VisualState.Setters>
                    <Setter Property="BackgroundColor" Value="Pink" />
                </VisualState.Setters>
            </VisualState>

            <VisualState Name="PointerOver">
                <VisualState.Setters>
                    <Setter Property="BackgroundColor" Value="LightBlue" />
                </VisualState.Setters>
            </VisualState>
        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>
</Entry>

На следующем снимке экрана показано Entry четыре определенных визуальных состояния:

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

Когда состояние Entry находится в состоянии, его фон является лайм Normal . При получении входного фокуса Entry его размер шрифта удвоится. Когда становится Entry отключенным, его фон становится розовым. Фон Entry лайма не сохраняется при получении фокуса ввода. Когда указатель мыши наведите указатель мыши на Entryнее, но не нажимается, Entry фон становится светло-синий. Так как диспетчер визуальных состояний переключается между визуальными состояниями, свойства, заданные предыдущим состоянием, не заданы. Таким образом, визуальные состояния являются взаимоисключающими.

Если вы хотите Entry , чтобы фон лайма был в Focused состоянии, добавьте еще один Setter в это визуальное состояние:

<VisualState Name="Focused">
    <VisualState.Setters>
        <Setter Property="FontSize" Value="36" />
        <Setter Property="BackgroundColor" Value="Lime" />
    </VisualState.Setters>
</VisualState>

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

Часто необходимо совместно использовать одни и те же визуальные состояния в двух или более представлениях. В этом сценарии визуальные состояния можно определить в Style. Это можно сделать, добавив Setter объект для VisualStateManager.VisualStateGroups свойства. Свойство содержимого для Setter объекта является его Value свойством, поэтому его можно указать как дочерний Setter объект. Свойство VisualStateGroups имеет тип VisualStateGroupList, поэтому дочерний Setter объект является VisualStateGroupList дочерним объектом, к которому VisualStateGroup можно добавить, который содержит VisualState объекты.

В следующем примере показан неявный стиль для определения Entry общих визуальных состояний:

<Style TargetType="Entry">
    <Setter Property="FontSize" Value="18" />
    <Setter Property="VisualStateManager.VisualStateGroups">
        <VisualStateGroupList>
            <VisualStateGroup Name="CommonStates">
                <VisualState Name="Normal">
                    <VisualState.Setters>
                        <Setter Property="BackgroundColor" Value="Lime" />
                    </VisualState.Setters>
                </VisualState>
                <VisualState Name="Focused">
                    <VisualState.Setters>
                        <Setter Property="FontSize" Value="36" />
                        <Setter Property="BackgroundColor" Value="Lime" />
                    </VisualState.Setters>
                </VisualState>
                <VisualState Name="Disabled">
                    <VisualState.Setters>
                        <Setter Property="BackgroundColor" Value="Pink" />
                    </VisualState.Setters>
                </VisualState>
                <VisualState Name="PointerOver">
                    <VisualState.Setters>
                        <Setter Property="BackgroundColor" Value="LightBlue" />
                    </VisualState.Setters>
                </VisualState>
            </VisualStateGroup>
        </VisualStateGroupList>
    </Setter>
</Style>

Если этот стиль включен в словарь ресурсов на уровне страницы, Style объект будет применен ко всем Entry объектам на странице. Таким образом, все Entry объекты на странице будут реагировать одинаково на их визуальные состояния.

Визуальные состояния в .NET MAUI

В следующей таблице перечислены визуальные состояния, определенные в .NET MAUI:

Класс Состояния Дополнительные сведения
Button Pressed Визуальные состояния кнопки
CarouselView DefaultItem, , CurrentItemPreviousItemNextItem Визуальные состояния CarouselView
CheckBox IsChecked Визуальные состояния CheckBox
CollectionView Selected Визуальные состояния CollectionView
ImageButton Pressed Визуальные состояния ImageButton
RadioButton Checked, Unchecked Визуальные состояния RadioButton
Switch On, Off Переключение визуальных состояний
VisualElement Normal, , DisabledFocusedPointerOver Общие состояния

Установка состояния для нескольких элементов

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

Тип Setter имеет TargetName свойство типа, stringпредставляющее целевой объект, с которым Setter будет управлять визуальное состояние. TargetName При определении свойства задает Property объект, Setter определенный в TargetNameValue:

<Setter TargetName="label"
        Property="Label.TextColor"
        Value="Red" />

В этом примере у именованного Labellabel свойства будет TextColor задано значение Red. При задании TargetName свойства необходимо указать полный путь к свойству.Property Таким образом, чтобы задать TextColor свойство в объекте Label, Property указывается как Label.TextColor.

Примечание.

Любое свойство, на которое Setter ссылается объект, должно поддерживаться привязываемым свойством.

В следующем примере показано, как задать состояние для нескольких объектов из одной визуальной группы состояний:

<StackLayout>
    <Label Text="What is the capital of France?" />
    <Entry x:Name="entry"
           Placeholder="Enter answer" />
    <Button Text="Reveal answer">
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup Name="CommonStates">
                <VisualState Name="Normal" />
                <VisualState Name="Pressed">
                    <VisualState.Setters>
                        <Setter Property="Scale"
                                Value="0.8" />
                        <Setter TargetName="entry"
                                Property="Entry.Text"
                                Value="Paris" />
                    </VisualState.Setters>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
    </Button>
</StackLayout>

В этом примере Normal состояние активно, если Button не нажимается, и в нее можно ввести Entryответ. Состояние Pressed становится активным при Button нажатии клавиши и указывает, что его Scale свойство будет изменено со значения по умолчанию от 1 до 0,8. Кроме того, именованный Entryentry будет иметь его Text свойство, установленное в Париже. Таким образом, результат заключается в том, что при Button нажатии его масштаб будет немного меньше, и Entry отображается Париж:

Снимок экрана: состояние нажатия кнопки.

Затем, когда Button он выпущен, он перемасштабируется до значения по умолчанию 1, и Entry отображает любой ранее введенный текст.

Внимание

Пути свойств не поддерживаются в Setter элементах, которые указывают TargetName свойство.

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

Пользовательские визуальные состояния можно реализовать, определив их так же, как вы определите визуальные состояния для общих состояний, но с именами выбранных состояний, а затем вызовом VisualStateManager.GoToState метода для активации состояния.

В следующем примере показано, как использовать Visual State Manager для проверки входных данных:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="VsmDemos.VsmValidationPage"
             Title="VSM Validation">
    <StackLayout x:Name="stackLayout"
                 Padding="10, 10">
            <VisualStateManager.VisualStateGroups>
                <VisualStateGroup Name="ValidityStates">
                    <VisualState Name="Valid">
                        <VisualState.Setters>
                            <Setter TargetName="helpLabel"
                                    Property="Label.TextColor"
                                    Value="Transparent" />
                            <Setter TargetName="entry"
                                    Property="Entry.BackgroundColor"
                                    Value="Lime" />
                        </VisualState.Setters>
                    </VisualState>
                    <VisualState Name="Invalid">
                        <VisualState.Setters>
                            <Setter TargetName="entry"
                                    Property="Entry.BackgroundColor"
                                    Value="Pink" />
                            <Setter TargetName="submitButton"
                                    Property="Button.IsEnabled"
                                    Value="False" />
                        </VisualState.Setters>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>
        <Label Text="Enter a U.S. phone number:"
               FontSize="18" />
        <Entry x:Name="entry"
               Placeholder="555-555-5555"
               FontSize="18"
               Margin="30, 0, 0, 0"
               TextChanged="OnTextChanged" />
        <Label x:Name="helpLabel"
               Text="Phone number must be of the form 555-555-5555, and not begin with a 0 or 1" />
        <Button x:Name="submitButton"
                Text="Submit"
                FontSize="18"
                Margin="0, 20"
                VerticalOptions="Center"
                HorizontalOptions="Center" />
    </StackLayout>
</ContentPage>

В этом примере визуальные состояния присоединены к StackLayoutи есть два взаимоисключающих состояния с именем Valid и Invalid. Entry Если номер телефона не содержит допустимый, то текущее состояние Invalidравно, и поэтому Entry имеется розовый фон, второй Label отображается и Button отключен. При вводе допустимого номера телефона текущее состояние становится Valid. Получает Entry фон лайма, второй Label исчезает и Button теперь включен:

Снимок экрана: пример проверки визуального состояния.

Файл программной части отвечает за обработку TextChanged события из файла Entry. Обработчик использует регулярное выражение, чтобы определить, является ли входная строка допустимой или нет. Метод GoToState в файле программной части вызывает статический VisualStateManager.GoToState метод объекта StackLayout :

public partial class VsmValidationPage : ContentPage
{
    public VsmValidationPage()
    {
        InitializeComponent();

        GoToState(false);
    }

    void OnTextChanged(object sender, TextChangedEventArgs args)
    {
        bool isValid = Regex.IsMatch(args.NewTextValue, @"^[2-9]\d{2}-\d{3}-\d{4}$");
        GoToState(isValid);
    }

    void GoToState(bool isValid)
    {
        string visualState = isValid ? "Valid" : "Invalid";
        VisualStateManager.GoToState(stackLayout, visualState);
    }
}

В этом примере GoToState метод вызывается из конструктора для инициализации состояния. Всегда должно быть текущее состояние. Затем файл программной части вызывается VisualStateManager.GoToStateс именем состояния в объекте, который определяет визуальные состояния.

Триггеры визуального состояния

Визуальные состояния поддерживают триггеры состояния, которые представляют собой специализированную группу триггеров, определяющих условия применения VisualState .

Триггеры состояния добавляются в коллекцию StateTriggersVisualState. Эта коллекция может содержать один или несколько триггеров состояния. При наличии активных триггеров состояния в коллекции будет применяться VisualState.

При использовании триггеров состояния для управления визуальными состояниями .NET MAUI использует следующие правила приоритета, чтобы определить, какой триггер (и соответствующий VisualState) будет активным:

  1. Любой триггер, производный от StateTriggerBase.
  2. AdaptiveTrigger активируется из-за выполнения условия MinWindowWidth.
  3. AdaptiveTrigger активируется из-за выполнения условия MinWindowHeight.

Если одновременно активны несколько триггеров (например, два пользовательских триггера), то у первого триггера, объявленного в разметке, будет приоритет.

Дополнительные сведения о триггерах состояния см. в разделе Триггеры состояния.