Xamarin.Forms Визуальный диспетчер состояний
Используйте диспетчер визуальных состояний для внесения изменений в элементы XAML на основе визуальных состояний, заданных из кода.
Visual State Manager (VSM) предоставляет структурированный способ внесения визуальных изменений в пользовательский интерфейс из кода. В большинстве случаев пользовательский интерфейс приложения определяется в XAML, и этот XAML включает разметку, описывающую, как диспетчер визуальных состояний влияет на визуальные элементы пользовательского интерфейса.
VSM представляет концепцию визуальных состояний. Представление Xamarin.Forms , например Button
, может иметь несколько различных внешних представлений в зависимости от его базового состояния — отключается ли оно или нажимается или имеет фокус ввода. Это состояния кнопки.
Визуальные состояния собираются в группах визуальных состояний. Все визуальные состояния в группе визуальных состояний являются взаимоисключающими. Визуальные состояния и группы визуальных состояний определяются простыми текстовыми строками.
Диспетчер Xamarin.Forms визуальных состояний определяет одну группу визуальных состояний с именем CommonStates со следующими визуальными состояниями:
- "Normal"
- "Отключено"
- "Сосредоточено"
- "Выбрано"
Эта группа визуальных состояний поддерживается для всех классов, производных от VisualElement
, который является базовым классом для View
и Page
.
Вы также можете определить собственные группы визуальных состояний и визуальные состояния, как показано в этой статье.
Примечание.
Xamarin.Forms Разработчики, знакомые с триггерами, знают, что триггеры также могут вносить изменения в визуальные элементы в пользовательском интерфейсе на основе изменений свойств представления или запуска событий. Однако использование триггеров для работы с различными сочетаниями этих изменений может стать довольно запутанным. Исторически диспетчер визуальных состояний был представлен в средах на основе Windows XAML, чтобы облегчить путаницу, полученную из сочетаний визуальных состояний. При использовании VSM визуальные состояния в группе визуальных состояний всегда являются взаимоисключающими. В любое время только одно состояние в каждой группе — текущее состояние.
Общие состояния
Диспетчер визуальных состояний позволяет включить разметку в XAML-файл, который может изменить внешний вид представления, если представление нормально или отключено или имеет фокус ввода. Они называются общими состояниями.
Например, предположим, что на странице есть Entry
представление, и вы хотите, чтобы внешний вид визуального Entry
элемента изменился следующим образом:
- Должен
Entry
иметь розовый фон приEntry
отключении. - Должен
Entry
иметь фон лайма обычно. - Должно
Entry
увеличиться до дважды его нормальной высоты, если он имеет фокус ввода.
Вы можете присоединить разметку VSM к отдельному представлению или определить ее в стиле, если он применяется к нескольким представлениям. В следующих двух разделах описаны эти подходы.
Разметка VSM в представлении
Чтобы присоединить разметку VSM к представлению Entry
, сначала разделите их на начальные Entry
и конечные теги:
<Entry FontSize="18">
</Entry>
Он получает явный размер шрифта, так как одно из состояний будет использовать FontSize
свойство для двойного размера текста в нем Entry
.
Затем вставьте VisualStateManager.VisualStateGroups
теги между этими тегами:
<Entry FontSize="18">
<VisualStateManager.VisualStateGroups>
</VisualStateManager.VisualStateGroups>
</Entry>
VisualStateGroups
является присоединенным привязываемым свойством, определенным классом VisualStateManager
. (Дополнительные сведения о присоединенных привязываемых свойствах см. в статье Присоединенные свойства.) Вот как VisualStateGroups
свойство присоединено к объекту Entry
.
Свойство VisualStateGroups
имеет тип VisualStateGroupList
, который является коллекцией VisualStateGroup
объектов. VisualStateManager.VisualStateGroups
В тегах вставьте пару тегов VisualStateGroup
для каждой группы визуальных состояний, которые необходимо включить:
<Entry FontSize="18">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Entry>
Обратите внимание, что VisualStateGroup
тег имеет атрибут, указывающий x:Name
имя группы. Класс VisualStateGroup
определяет Name
свойство, которое можно использовать вместо этого:
<VisualStateGroup Name="CommonStates">
В одном элементе можно использовать x:Name
либо или Name
не оба элемента.
Класс VisualStateGroup
определяет свойство с именем States
, которое является коллекцией VisualState
объектов. States
— это свойство содержимогоVisualStateGroups
, которое позволяет включать VisualState
теги непосредственно между VisualStateGroup
тегами. (Свойства содержимого рассматриваются в статье Основной синтаксис XAML.)
Следующим шагом является включение пары тегов для каждого визуального состояния в этой группе. Их также можно определить с помощью x:Name
или Name
:
<Entry FontSize="18">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
</VisualState>
<VisualState x:Name="Focused">
</VisualState>
<VisualState x:Name="Disabled">
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Entry>
VisualState
определяет свойство с именем Setters
, которое является коллекцией Setter
объектов. Это те же Setter
объекты, которые используются в объекте Style
.
Setters
не является свойством содержимогоVisualState
, поэтому необходимо включить теги элементов свойства для Setters
свойства:
<Entry FontSize="18">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<VisualState.Setters>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Focused">
<VisualState.Setters>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Disabled">
<VisualState.Setters>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Entry>
Теперь можно вставить один или несколько Setter
объектов между каждой парой тегов Setters
. Это объекты, определяющие визуальные Setter
состояния, описанные ранее:
<Entry FontSize="18">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="Lime" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Focused">
<VisualState.Setters>
<Setter Property="FontSize" Value="36" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="Pink" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Entry>
Каждый Setter
тег указывает значение определенного свойства при текущем состоянии. Любое свойство, на которое Setter
ссылается объект, должно поддерживаться привязываемым свойством.
Разметка , аналогичная этой, является основой страницы VSM на странице представления в примере программы. Страница содержит три Entry
представления, но только второй содержит разметку VSM, подключенную к ней:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:VsmDemos"
x:Class="VsmDemos.MainPage"
Title="VSM Demos">
<StackLayout>
<StackLayout.Resources>
<Style TargetType="Entry">
<Setter Property="Margin" Value="20, 0" />
<Setter Property="FontSize" Value="18" />
</Style>
<Style TargetType="Label">
<Setter Property="Margin" Value="20, 30, 20, 0" />
<Setter Property="FontSize" Value="Large" />
</Style>
</StackLayout.Resources>
<Label Text="Normal Entry:" />
<Entry />
<Label Text="Entry with VSM: " />
<Entry>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="Lime" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Focused">
<VisualState.Setters>
<Setter Property="FontSize" Value="36" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="Pink" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Entry.Triggers>
<DataTrigger TargetType="Entry"
Binding="{Binding Source={x:Reference entry3},
Path=Text.Length}"
Value="0">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Entry.Triggers>
</Entry>
<Label Text="Entry to enable 2nd Entry:" />
<Entry x:Name="entry3"
Text=""
Placeholder="Type something to enable 2nd Entry" />
</StackLayout>
</ContentPage>
Обратите внимание, что второй Entry
также имеет DataTrigger
часть своей Trigger
коллекции. Это приводит Entry
к отключению, пока что-то не будет введено в третью Entry
. Ниже приведена страница при запуске в iOS, Android и универсальная платформа Windows (UWP):
Текущее визуальное состояние "Отключено", поэтому фон второго Entry
розовый на экранах iOS и Android. Реализация UWP Entry
не позволяет задать цвет фона при Entry
отключении.
При вводе текста в третий Entry
, второй Entry
переключается в состояние "Обычный", а фон теперь является лаймом:
При касании второго Entry
он получает фокус ввода. Он переключается на состояние "Фокус" и расширяется до дважды его высоты:
Обратите внимание, что фон Entry
лайма не сохраняется при получении фокуса ввода. Так как диспетчер визуальных состояний переключается между визуальными состояниями, свойства, заданные предыдущим состоянием, не заданы. Помните, что визуальные состояния являются взаимоисключающими. Состояние "Обычный" не означает только то, что Entry
он включен. Это означает, что включен Entry
и не имеет фокуса ввода.
Если вы хотите Entry
, чтобы фон лайма был в состоянии "Фокус", добавьте еще один Setter
в это визуальное состояние:
<VisualState x:Name="Focused">
<VisualState.Setters>
<Setter Property="FontSize" Value="36" />
<Setter Property="BackgroundColor" Value="Lime" />
</VisualState.Setters>
</VisualState>
Чтобы эти Setter
объекты работали правильно, VisualStateGroup
необходимо содержать VisualState
объекты для всех состояний в этой группе. Если есть визуальное состояние, которое не имеет Setter
объектов, добавьте его в любом случае в качестве пустого тега:
<VisualState x:Name="Normal" />
Разметка Visual State Manager в стиле
Часто необходимо совместно использовать одну разметку диспетчера визуальных состояний между двумя или более представлениями. В этом случае необходимо поместить разметку в Style
определение.
Ниже приведены неявные Style
элементы Entry
на странице представления VSM:
<Style TargetType="Entry">
<Setter Property="Margin" Value="20, 0" />
<Setter Property="FontSize" Value="18" />
</Style>
Добавьте Setter
теги для присоединенного VisualStateManager.VisualStateGroups
привязываемого свойства:
<Style TargetType="Entry">
<Setter Property="Margin" Value="20, 0" />
<Setter Property="FontSize" Value="18" />
<Setter Property="VisualStateManager.VisualStateGroups">
</Setter>
</Style>
Свойство содержимого Value
для Setter
имеет значениеValue
, поэтому значение свойства можно указать непосредственно в этих тегах. Это свойство имеет тип VisualStateGroupList
:
<Style TargetType="Entry">
<Setter Property="Margin" Value="20, 0" />
<Setter Property="FontSize" Value="18" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
</VisualStateGroupList>
</Setter>
</Style>
В этих тегах можно включить один из нескольких VisualStateGroup
объектов:
<Style TargetType="Entry">
<Setter Property="Margin" Value="20, 0" />
<Setter Property="FontSize" Value="18" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
Оставшаяся часть разметки VSM совпадает с тем же, что и раньше.
Ниже приведена страница VSM в стиле , на которой показана полная разметка VSM:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="VsmDemos.VsmInStylePage"
Title="VSM in Style">
<StackLayout>
<StackLayout.Resources>
<Style TargetType="Entry">
<Setter Property="Margin" Value="20, 0" />
<Setter Property="FontSize" Value="18" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="Lime" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Focused">
<VisualState.Setters>
<Setter Property="FontSize" Value="36" />
<Setter Property="BackgroundColor" Value="Lime" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="Pink" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="Label">
<Setter Property="Margin" Value="20, 30, 20, 0" />
<Setter Property="FontSize" Value="Large" />
</Style>
</StackLayout.Resources>
<Label Text="Normal Entry:" />
<Entry />
<Label Text="Entry with VSM: " />
<Entry>
<Entry.Triggers>
<DataTrigger TargetType="Entry"
Binding="{Binding Source={x:Reference entry3},
Path=Text.Length}"
Value="0">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Entry.Triggers>
</Entry>
<Label Text="Entry to enable 2nd Entry:" />
<Entry x:Name="entry3"
Text=""
Placeholder="Type something to enable 2nd Entry" />
</StackLayout>
</ContentPage>
Теперь все Entry
представления на этой странице реагируют одинаково на их визуальные состояния. Обратите внимание также, что состояние "Фокус" теперь включает в себя секунду Setter
, которая дает каждому Entry
фону лайма, также если он имеет фокус ввода:
Визуальные состояния в Xamarin.Forms
В следующей таблице перечислены визуальные состояния, определенные в Xamarin.Forms:
Класс | Состояния | Дополнительные сведения |
---|---|---|
Button |
Pressed |
Визуальные состояния кнопки |
CheckBox |
IsChecked |
Визуальные состояния CheckBox |
CarouselView |
DefaultItem , , CurrentItem PreviousItem NextItem |
Визуальные состояния CarouselView |
ImageButton |
Pressed |
Визуальные состояния ImageButton |
RadioButton |
Checked , Unchecked |
Визуальные состояния RadioButton |
Switch |
On , Off |
Переключение визуальных состояний |
VisualElement |
Normal , , Disabled Focused Selected |
Общие состояния |
Доступ к каждому из этих состояний можно получить через группу визуальных состояний с именем CommonStates
.
Кроме того, он CollectionView
реализует Selected
состояние. Дополнительные сведения см. в разделе "Изменение цвета выбранного элемента".
Установка состояния для нескольких элементов
В предыдущих примерах визуальные состояния были присоединены и работали с одними элементами. Однако также можно создать визуальные состояния, присоединенные к одному элементу, но которые задают свойства для других элементов в той же области. Это позволяет избежать необходимости повторять визуальные состояния для каждого элемента, над которыми работают состояния.
Тип Setter
имеет TargetName
свойство типа, string
представляющее целевой элемент, который Setter
будет управлять визуальным состоянием. TargetName
При определении свойства задает Property
элемент, Setter
определенный в TargetName
Value
:
<Setter TargetName="label"
Property="Label.TextColor"
Value="Red" />
В этом примере у именованного Label
label
свойства будет TextColor
задано значение Red
. При задании TargetName
свойства необходимо указать полный путь к свойству.Property
Таким образом, чтобы задать TextColor
свойство в объекте Label
, Property
указывается как Label.TextColor
.
Примечание.
Любое свойство, на которое Setter
ссылается объект, должно поддерживаться привязываемым свойством.
На странице VSM с Setter TargetName в примере показано, как задать состояние для нескольких элементов из одной визуальной группы состояний. XAML-файл состоит из StackLayout
элементаLabel
, элемента, а Entry
также :Button
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="VsmDemos.VsmSetterTargetNamePage"
Title="VSM with Setter TargetName">
<StackLayout Margin="10">
<Label Text="What is the capital of France?" />
<Entry x:Name="entry"
Placeholder="Enter answer" />
<Button Text="Reveal answer">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x: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>
</ContentPage>
Разметка VSM присоединена к StackLayout
. Существует два взаимоисключающих состояния с именем "Normal" и "Pressed", с каждым состоянием VisualState
, содержащим теги.
Состояние "Обычный" активируется, если Button
нажатие не нажато, и можно ввести ответ на вопрос:
Состояние "Нажато" становится активным при Button
нажатии:
VisualState
Нажатие клавиши указывает, что при Button
нажатии его Scale
свойство будет изменено со значения по умолчанию от 1 до 0,8. Кроме того, именованный Entry
entry
будет иметь его Text
свойство, установленное в Париже. Таким образом, результат заключается в том, что при Button
нажатии он перемасштабируется, чтобы быть немного меньше, и Entry
отображение Парижа. Затем, когда Button
он выпущен, он перемасштабируется до значения по умолчанию 1, и Entry
отображает любой ранее введенный текст.
Внимание
Пути свойств в настоящее время не поддерживаются в Setter
элементах, которые указывают TargetName
свойство.
Определение собственных визуальных состояний
Каждый класс, производный от VisualElement
поддержки общих состояний "Normal", "Focused" и "Disabled". Кроме того, CollectionView
класс поддерживает состояние Selected. VisualElement
Внутри него класс обнаруживает, когда он становится включенным или отключенным, а также сосредоточен или не ориентирован, а также вызывает статический VisualStateManager.GoToState
метод:
VisualStateManager.GoToState(this, "Focused");
Это единственный код Visual State Manager, который вы найдете в VisualElement
классе. Так как GoToState
вызывается для каждого объекта на основе каждого класса, наследуемого от VisualElement
каждого класса, можно использовать диспетчер визуальных состояний с любым VisualElement
объектом для реагирования на эти изменения.
Интересно, что имя визуальной группы состояний CommonStates не ссылается явным образом.VisualElement
Имя группы не является частью API для диспетчера визуальных состояний. В одной из двух примеров программы, показанной до сих пор, вы можете изменить имя группы с CommonStates на что-либо другое, и программа по-прежнему будет работать. Имя группы — это просто общее описание состояний в этой группе. Неявно понятно, что визуальные состояния в любой группе являются взаимоисключающими: одно состояние и только одно состояние в любое время является текущим.
Если вы хотите реализовать собственные визуальные состояния, необходимо вызвать VisualStateManager.GoToState
из кода. Чаще всего этот вызов выполняется из файла программной части класса страницы.
На странице проверки VSM в примере показано, как использовать визуальный диспетчер состояний в связи с проверкой входных данных. XAML-файл состоит из StackLayout
двух Label
элементов, а Entry
также :Button
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
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="Large" />
<Entry x:Name="entry"
Placeholder="555-555-5555"
FontSize="Large"
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="Large"
Margin="0, 20"
VerticalOptions="Center"
HorizontalOptions="Center" />
</StackLayout>
</ContentPage>
Разметка VSM присоединена к StackLayout
(именованному stackLayout
). Существует два взаимоисключающих состояния с именем "Допустимый" и "Недопустимый" с каждым состоянием VisualState
, содержащим теги.
Entry
Если номер телефона не содержит допустимый, текущее состояние равно "Недопустимо" и поэтому Entry
имеет розовый фон, второй Label
отображается и Button
отключен:
При вводе допустимого номера телефона текущее состояние становится допустимым. Получает 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
метод вызывается из конструктора для инициализации состояния. Всегда должно быть текущее состояние. Но нигде в коде нет ссылки на имя визуальной группы состояний, хотя она ссылается на XAML как "ValidationStates" для ясности.
Обратите внимание, что файл программной части должен учитывать только объект на странице, определяющей визуальные состояния, и вызывать VisualStateManager.GoToState
этот объект. Это связано с тем, что оба визуальных состояния предназначены для нескольких объектов на странице.
Вы можете задаться вопросом: если файл кода программной части должен ссылаться на объект на странице, определяющей визуальные состояния, почему файл программной части не может просто получить доступ к этому и другим объектам напрямую? Это, безусловно, может. Однако преимущество использования VSM заключается в том, что визуальные элементы могут управлять тем, как визуальные элементы реагируют на другое состояние полностью в XAML, что сохраняет весь дизайн пользовательского интерфейса в одном расположении. Это позволяет избежать настройки внешнего вида, доступ к визуальным элементам непосредственно из программной части.
Триггеры визуального состояния
Визуальные состояния поддерживают триггеры состояния, которые представляют собой специализированную группу триггеров, определяющих условия применения VisualState
.
Триггеры состояния добавляются в коллекцию StateTriggers
VisualState
. Эта коллекция может содержать один или несколько триггеров состояния. При наличии активных триггеров состояния в коллекции будет применяться VisualState
.
При использовании триггеров состояния для управления визуальными состояниями Xamarin.Forms применяет следующие правила приоритета, чтобы определить, какой триггер (и соответственно VisualState
) будет активен:
- Любой триггер, производный от
StateTriggerBase
. AdaptiveTrigger
активируется из-за выполнения условияMinWindowWidth
.AdaptiveTrigger
активируется из-за выполнения условияMinWindowHeight
.
Если одновременно активны несколько триггеров (например, два пользовательских триггера), то у первого триггера, объявленного в разметке, будет приоритет.
Дополнительные сведения о триггерах состояния см. в разделе Триггеры состояния.
Использование диспетчера визуальных состояний для адаптивного макета
Приложение Xamarin.Forms , работающее на телефоне, обычно можно просматривать в книжном или альбомном пропорции, а Xamarin.Forms программа, работающая на рабочем столе, может быть изменена, чтобы предположить множество различных размеров и пропорций. Хорошо разработанное приложение может отображать его содержимое по-разному для этих различных форм-факторов страницы или окна.
Этот метод иногда называется адаптивным макетом. Так как адаптивный макет исключительно включает визуальные элементы программы, это идеальное приложение диспетчера визуальных состояний.
Простой пример — это приложение, отображающее небольшую коллекцию кнопок, влияющих на содержимое приложения. В книжном режиме эти кнопки могут отображаться в горизонтальной строке в верхней части страницы:
В альбомном режиме массив кнопок может перемещаться на одну сторону и отображаться в столбце:
Сверху вниз программа выполняется в универсальная платформа Windows, Android и iOS.
Страница адаптивного макета VSM в примере определяет группу с именем "OrientationStates" с двумя визуальными состояниями "Книжный" и "Альбом". (Более сложный подход может быть основан на нескольких разных ширинах страницы или окна.)
Разметка VSM выполняется в четырех местах в XAML-файле. Имя StackLayout
mainStack
содержит как меню, так и содержимое, которое является элементом Image
. Это StackLayout
должно иметь вертикальную ориентацию в книжном режиме и горизонтальной ориентации в альбомном режиме:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="VsmDemos.VsmAdaptiveLayoutPage"
Title="VSM Adaptive Layout">
<StackLayout x:Name="mainStack">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="OrientationStates">
<VisualState Name="Portrait">
<VisualState.Setters>
<Setter Property="Orientation" Value="Vertical" />
</VisualState.Setters>
</VisualState>
<VisualState Name="Landscape">
<VisualState.Setters>
<Setter Property="Orientation" Value="Horizontal" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ScrollView x:Name="menuScroll">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="OrientationStates">
<VisualState Name="Portrait">
<VisualState.Setters>
<Setter Property="Orientation" Value="Horizontal" />
</VisualState.Setters>
</VisualState>
<VisualState Name="Landscape">
<VisualState.Setters>
<Setter Property="Orientation" Value="Vertical" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<StackLayout x:Name="menuStack">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="OrientationStates">
<VisualState Name="Portrait">
<VisualState.Setters>
<Setter Property="Orientation" Value="Horizontal" />
</VisualState.Setters>
</VisualState>
<VisualState Name="Landscape">
<VisualState.Setters>
<Setter Property="Orientation" Value="Vertical" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<StackLayout.Resources>
<Style TargetType="Button">
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup Name="OrientationStates">
<VisualState Name="Portrait">
<VisualState.Setters>
<Setter Property="HorizontalOptions" Value="CenterAndExpand" />
<Setter Property="Margin" Value="10, 5" />
</VisualState.Setters>
</VisualState>
<VisualState Name="Landscape">
<VisualState.Setters>
<Setter Property="VerticalOptions" Value="CenterAndExpand" />
<Setter Property="HorizontalOptions" Value="Center" />
<Setter Property="Margin" Value="10" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
</StackLayout.Resources>
<Button Text="Banana"
Command="{Binding SelectedCommand}"
CommandParameter="Banana.jpg" />
<Button Text="Face Palm"
Command="{Binding SelectedCommand}"
CommandParameter="FacePalm.jpg" />
<Button Text="Monkey"
Command="{Binding SelectedCommand}"
CommandParameter="monkey.png" />
<Button Text="Seated Monkey"
Command="{Binding SelectedCommand}"
CommandParameter="SeatedMonkey.jpg" />
</StackLayout>
</ScrollView>
<Image x:Name="image"
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand" />
</StackLayout>
</ContentPage>
Внутреннее ScrollView
имя menuScroll
и StackLayout
именованное menuStack
меню кнопок. Ориентация этих макетов отличается от mainStack
. Меню должно быть горизонтально в книжном режиме и вертикальном режиме в альбомном режиме.
Четвертый раздел разметки VSM находится в неявном стиле для кнопок. Эти наборы VerticalOptions
HorizontalOptions
разметки и Margin
свойства, относящиеся к книжной и альбомной ориентации.
Файл программной части задает BindingContext
свойство menuStack
реализации Button
команд, а также присоединяет обработчик к SizeChanged
событию страницы:
public partial class VsmAdaptiveLayoutPage : ContentPage
{
public VsmAdaptiveLayoutPage ()
{
InitializeComponent ();
SizeChanged += (sender, args) =>
{
string visualState = Width > Height ? "Landscape" : "Portrait";
VisualStateManager.GoToState(mainStack, visualState);
VisualStateManager.GoToState(menuScroll, visualState);
VisualStateManager.GoToState(menuStack, visualState);
foreach (View child in menuStack.Children)
{
VisualStateManager.GoToState(child, visualState);
}
};
SelectedCommand = new Command<string>((filename) =>
{
image.Source = ImageSource.FromResource("VsmDemos.Images." + filename);
});
menuStack.BindingContext = this;
}
public ICommand SelectedCommand { private set; get; }
}
Обработчик SizeChanged
вызывает VisualStateManager.GoToState
два StackLayout
элемента и ScrollView
элементы, а затем выполняет цикл по дочерним menuStack
элементам для Button
вызова VisualStateManager.GoToState
элементов.
Может показаться, что если файл кода программной части может обрабатывать изменения ориентации более непосредственно путем задания свойств элементов в XAML-файле, но визуальный диспетчер состояний, безусловно, более структурированный подход. Все визуальные элементы хранятся в XAML-файле, где они становятся проще изучить, поддерживать и изменять.
Visual State Manager с Xamarin.University
Xamarin.Forms Видео диспетчера визуальных состояний 3.0