Administrador de estado visual de Xamarin.Forms
Usa el Administrador de estado visual para realizar cambios en elementos XAML basados en estados visuales establecidos desde el código.
Visual State Manager (VSM) proporciona una manera estructurada de realizar cambios visuales en la interfaz de usuario desde el código. En la mayoría de los casos, la interfaz de usuario de la aplicación se define en XAML y este XAML incluye marcado que describe cómo afecta Visual State Manager a los objetos visuales de la interfaz de usuario.
VSM presenta el concepto de estados visuales. Una vista Xamarin.Forms, como un Button
, puede tener varias apariencias visuales diferentes en función de su estado subyacente, tanto si está deshabilitada como si está presionada o tiene el foco de entrada. Ests son los estados del botón.
Los estados visuales se recopilan en grupos de estados visuales. Todos los estados visuales dentro de un grupo de estados visuales son mutuamente excluyentes. Tanto los estados visuales como los grupos de estados visuales se identifican con cadenas de texto simples.
El Administrador de estado visual de Xamarin.Forms define un grupo de estado visual denominado "CommonStates" con los siguientes estados visuales:
- "Normal"
- "Deshabilitado"
- "Enfocado"
- "Seleccionado"
Este grupo de estado visual se admite para todas las clases que derivan de VisualElement
, que es la clase base para View
y Page
.
También puede definir sus propios grupos de estados visuales y estados visuales, como se muestra en este artículo.
Nota:
Xamarin.Forms desarrolladores familiarizados con desencadenadores son conscientes de que los desencadenadores también pueden realizar cambios en los objetos visuales de la interfaz de usuario en función de los cambios en las propiedades de una vista o en la activación de eventos. Sin embargo, el uso de desencadenadores para tratar varias combinaciones de estos cambios puede resultar bastante confuso. Históricamente, el Administrador de estado visual se introdujo en entornos basados en XAML de Windows para aliviar la confusión resultante de combinaciones de estados visuales. Con VSM, los estados visuales dentro de un grupo de estados visuales siempre son mutuamente excluyentes. En cualquier momento, solo un estado de cada grupo es el estado actual.
Pasos comunes
El Administrador de estado visual permite incluir marcado en el archivo XAML que puede cambiar la apariencia visual de una vista si la vista es normal o deshabilitada o tiene el foco de entrada. Se conocen como estados comunes.
Por ejemplo, supongamos que tienes una vista Entry
en la página y deseas que la apariencia visual del objeto Entry
cambie de las maneras siguientes:
Entry
debe tener un fondo rosa cuandoEntry
está deshabilitado.Entry
debe tener un fondo de color lima normalmente.Entry
debe expandirse a dos veces su altura normal cuando tiene el foco de entrada.
Puede adjuntar el marcado de VSM a una vista individual o definirlo en un estilo si se aplica a varias vistas. En las dos secciones siguientes se describen estos enfoques.
Marcado de VSM en una vista
Para asociar el marcado de VSM a una vista de Entry
, primero separe las etiquetas Entry
de inicio y de finalización:
<Entry FontSize="18">
</Entry>
Se proporciona un tamaño de fuente explícito porque uno de los estados usará la propiedad FontSize
para duplicar el tamaño del texto en Entry
.
A continuación, inserte etiquetas VisualStateManager.VisualStateGroups
entre estas:
<Entry FontSize="18">
<VisualStateManager.VisualStateGroups>
</VisualStateManager.VisualStateGroups>
</Entry>
VisualStateGroups
es una propiedad enlazable adjunta definida por la clase VisualStateManager
. (Para más información sobre las propiedades adjuntas, consulte el artículo Introducción a las propiedades adjuntas). Así es como se adjunta la propiedad VisualStateGroups
al objeto Entry
.
La propiedad VisualStateGroups
es de tipo VisualStateGroupList
, que es una colección de objetos VisualStateGroup
. Dentro de las etiquetas VisualStateManager.VisualStateGroups
, inserte un par de etiquetas VisualStateGroup
para cada grupo de estados visuales que desee incluir:
<Entry FontSize="18">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Entry>
Observe que la etiqueta VisualStateGroup
tiene un atributo x:Name
que indica el nombre del grupo. La clase VisualStateGroup
define una propiedad Name
que puede usar en su lugar:
<VisualStateGroup Name="CommonStates">
Puede usar x:Name
o Name
, pero no ambos en el mismo elemento.
La clase VisualStateGroup
también hereda una propiedad denominada States
, que es una colección de objetos VisualState
. States
es la propiedad de contenido de VisualStateGroups
para que pueda incluir las etiquetas VisualState
directamente entre las etiquetas VisualStateGroup
. (Las propiedades de contenido se describen en el artículo Sintaxis XAML esencial).
El siguiente paso consiste en incluir un par de etiquetas para cada estado visual de ese grupo. También se pueden identificar mediante x:Name
o 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
define una propiedad denominada Setters
, que es una colección de objetos Setter
. Son los mismos objetos Setter
que se usan en un objeto Style
.
Setters
no es la propiedad de contenido de VisualState
, por lo que es necesario incluir etiquetas de elemento de propiedad para la propiedad 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>
Ahora puede insertar uno o varios objetos Setter
entre cada par de etiquetas Setters
. Estos son los objetos Setter
que definen los estados visuales descritos anteriormente:
<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>
Cada etiqueta Setter
indica el valor de una propiedad determinada cuando ese estado es actual. Cualquier propiedad a la que hace referencia un objeto Setter
debe estar respaldada por una propiedad enlazable.
Un marcado similar a este es la base de la página VSM en vista del programa de ejemplo. La página incluye tres vistas Entry
, pero solo la segunda tiene el marcado VSM asociado:
<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>
Observe que el segundo Entry
también tiene como DataTrigger
parte de su colección Trigger
. Esto hace que Entry
se deshabilite hasta que se escriba algo en el tercer Entry
. Esta es la página en el inicio que se ejecuta en iOS, Android y la Plataforma universal de Windows (UWP):
El estado visual actual es "Deshabilitado", por lo que el fondo del segundo Entry
es rosa en las pantallas iOS y Android. La implementación de UWP de Entry
no permite establecer el color de fondo cuando Entry
está deshabilitado.
Al escribir texto en el tercer Entry
, el segundo Entry
cambia al estado "Normal" y el fondo es ahora verde lima:
Al tocar el segundo Entry
, obtiene el foco de entrada. Cambia al estado "Centrado" y se expande dos veces su altura:
Observe que el Entry
no conserva el fondo verde lima cuando obtiene el foco de entrada. A medida que el Administrador de estado visual cambia entre los estados visuales, las propiedades establecidas por el estado anterior no se establecen. Tenga en cuenta que los estados visuales son mutuamente excluyentes. El estado "Normal" no significa únicamente que Entry
está habilitado. Significa que Entry
está habilitado y no tiene el foco de entrada.
Si quiere que Entry
tenga un fondo verde lima en el estado "Centrado", agregue otro Setter
a ese estado visual:
<VisualState x:Name="Focused">
<VisualState.Setters>
<Setter Property="FontSize" Value="36" />
<Setter Property="BackgroundColor" Value="Lime" />
</VisualState.Setters>
</VisualState>
Para que estos objetos Setter
funcionen correctamente, VisualStateGroup
debe contener objetos VisualState
para todos los estados de ese grupo. Si hay un estado visual que no tiene ningún objeto Setter
, debe incluirse como una etiqueta vacía:
<VisualState x:Name="Normal" />
Marcado de Visual State Manager en un estilo
A menudo es necesario compartir el mismo marcado de Visual State Manager entre dos o más vistas. En este caso, querrá colocar el marcado en una definición de Style
.
Este es el implícito Style
existente para los elementos Entry
de la página VSM en vista:
<Style TargetType="Entry">
<Setter Property="Margin" Value="20, 0" />
<Setter Property="FontSize" Value="18" />
</Style>
Agregue etiquetas Setter
para la propiedad enlazable adjunta VisualStateManager.VisualStateGroups
:
<Style TargetType="Entry">
<Setter Property="Margin" Value="20, 0" />
<Setter Property="FontSize" Value="18" />
<Setter Property="VisualStateManager.VisualStateGroups">
</Setter>
</Style>
La propiedad de contenido de Setter
es Value
, por lo que el valor de la propiedad Value
se puede especificar directamente dentro de esas etiquetas. Esa propiedad es de tipo VisualStateGroupList
:
<Style TargetType="Entry">
<Setter Property="Margin" Value="20, 0" />
<Setter Property="FontSize" Value="18" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
</VisualStateGroupList>
</Setter>
</Style>
Dentro de esas etiquetas puede incluir uno de los objetos VisualStateGroup
siguientes:
<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>
El resto del marcado VSM es el mismo que antes.
Esta es la página VSM en estilo que muestra el marcado VSM completo:
<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>
Ahora todas las vistas Entry
de esta página responden de la misma manera a sus estados visuales. Observe también que el estado "Centrado" ahora incluye un segundo Setter
que proporciona cada Entry
un fondo verde lima también cuando tiene el foco de entrada:
Estados visuales en Xamarin.Forms
En la tabla siguiente se muestran los estados visuales que se definen en Xamarin.Forms:
Clase | States | Más información |
---|---|---|
Button |
Pressed |
Estados visuales del botón |
CheckBox |
IsChecked |
Estados visuales de CheckBox |
CarouselView |
DefaultItem , CurrentItem , PreviousItem , NextItem |
Estados visuales de CarouselView |
ImageButton |
Pressed |
Estados visuales de ImageButton |
RadioButton |
Checked , Unchecked |
Estados visuales de RadioButton |
Switch |
On , Off |
Cambiar estados visuales |
VisualElement |
Normal , Disabled , Focused , Selected |
Pasos comunes |
Se puede acceder a cada uno de estos estados a través del grupo de estado visual denominado CommonStates
.
Además, CollectionView
implementa el estado Selected
. Para obtener más información, consulte Cambiar el color del elemento seleccionado.
Establecer el estado en varios elementos
En los ejemplos anteriores, los estados visuales se adjuntaron y operaron en elementos individuales. Sin embargo, también es posible crear estados visuales adjuntos a un único elemento, pero que establecen propiedades en otros elementos dentro del mismo ámbito. Esto evita tener que repetir estados visuales en cada elemento en el que operan los estados.
El tipo Setter
tiene una propiedad TargetName
, de tipo string
, que representa el elemento de destino que manipulará el Setter
para un estado visual. Cuando se define la propiedad TargetName
, el Setter
establece el Property
del elemento definido en TargetName
en Value
:
<Setter TargetName="label"
Property="Label.TextColor"
Value="Red" />
En este ejemplo, un Label
denominado label
tendrá su propiedad TextColor
establecida en Red
. Al establecer la propiedad TargetName
, debe especificar la ruta de acceso completa a la propiedad en Property
. Por lo tanto, para establecer la propiedad TextColor
en Label
, Property
se especifica como Label.TextColor
.
Nota:
Cualquier propiedad a la que hace referencia un objeto Setter
debe estar respaldada por una propiedad enlazable.
La página VSM con Setter TargetName del ejemplo muestra cómo establecer el estado en varios elementos desde un único grupo de estados visuales. El archivo XAML consta de un StackLayout
que contiene un elemento Label
, un Entry
, y un 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>
El marcado de VSM está asociado a StackLayout
. Hay dos estados mutuamente excluyentes, denominados "Normal" y "Presionado", con cada estado que contiene etiquetas VisualState
.
El estado "Normal" está activo cuando no se presiona el Button
y se puede escribir una respuesta a la pregunta:
El estado "Presionado" se activa cuando se presiona Button
:
El VisualState
"Presionado" especifica que cuando se presiona Button
, su propiedad Scale
se cambiará del valor predeterminado de 1 a 0,8. Además, Entry
con nombre entry
tendrá su propiedad Text
establecida en París. Por lo tanto, el resultado es que cuando se presiona el Button
se vuelve a escalar para que sea ligeramente más pequeño y el Entry
muestra París. Después, cuando Button
se libera, se vuelve a escalar a su valor predeterminado de 1 y Entry
muestra cualquier texto escrito anteriormente.
Importante
Las rutas de acceso de propiedad no se admiten actualmente en elementos Setter
que especifican la propiedad TargetName
.
Definición de los propios estados visuales
Cada clase que deriva de VisualElement
admite los estados comunes "Normal", "Enfocado" y "Deshabilitado". Además, la clase CollectionView
admite el estado "Seleccionado". Internamente, la clase VisualElement
detecta cuándo se habilita o deshabilita, o se centra o no se centra, y llama al método estático VisualStateManager.GoToState
:
VisualStateManager.GoToState(this, "Focused");
Este es el único código de Visual State Manager que encontrará en la clase VisualElement
. Dado que GoToState
se llama a para cada objeto basado en cada clase que deriva de VisualElement
, puede usar el Administrador de estado visual con cualquier objeto VisualElement
para responder a estos cambios.
Resulta interesante que el nombre del grupo de estado visual "CommonStates" no se hace referencia explícitamente en VisualElement
. El nombre del grupo no forma parte de la API del Administrador de estado visual. Dentro de uno de los dos programas de ejemplo mostrados hasta ahora, puede cambiar el nombre del grupo de "CommonStates" a cualquier otra cosa, y el programa seguirá funcionando. El nombre del grupo es simplemente una descripción general de los estados de ese grupo. Se entiende implícitamente que los estados visuales de cualquier grupo son mutuamente excluyentes: un estado y solo un estado está activo en cualquier momento.
Si quiere implementar sus propios estados visuales, deberá llamar VisualStateManager.GoToState
desde el código. Con más frecuencia, realizará esta llamada desde el archivo de código subyacente de la clase de página.
En la página Validación de VSM del ejemplo se muestra cómo usar el Administrador de estado visual en relación con la validación de entrada. El archivo XAML consta de un StackLayout
que contiene dos elementos Label
, un Entry
y un 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>
El marcado de VSM está asociado a StackLayout
(denominado stackLayout
). Hay dos estados mutuamente excluyentes, denominados "Normal" y "Presionado", con cada estado que contiene etiquetas VisualState
.
Si el Entry
no contiene un número de teléfono válido, el estado actual es "No válido", por lo que el Entry
tiene un fondo rosa, el segundo Label
está visible y el Button
está deshabilitado:
Cuando se escribe un número de teléfono válido, el estado actual se convierte en "Válido". Entry
tiene un fondo verde lima, el segundo Label
desaparece y Button
ahora está habilitado:
El archivo de código subyacente es responsable de controlar el evento TextChanged
desde Entry
El controlador usa una expresión regular para determinar si la cadena de entrada es válida o no. El método del archivo de código subyacente denominado GoToState
llama al método estático VisualStateManager.GoToState
para 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);
}
}
Observe también que se llama al método GoToState
desde el constructor para inicializar el estado. Siempre debe haber un estado actual. Pero en ninguna parte del código hay ninguna referencia al nombre del grupo de estado visual, aunque se hace referencia a ella en el XAML como "ValidationStates" para fines de claridad.
Observe que el archivo de código subyacente solo debe tener en cuenta el objeto de la página que define los estados visuales y llamar a VisualStateManager.GoToState
para este objeto. Esto se debe a que ambos estados visuales tienen como destino varios objetos en la página.
Es posible que se pregunte: Si el archivo de código subyacente debe hacer referencia al objeto en la página que define los estados visuales, ¿por qué el archivo de código subyacente simplemente puede acceder a este y a otros objetos directamente? Seguramente podría. Sin embargo, la ventaja de usar VSM es que puedes controlar cómo reaccionan los elementos visuales a un estado diferente completamente en XAML, lo que mantiene todo el diseño de la interfaz de usuario en una ubicación. Esto evita establecer la apariencia visual accediendo a los elementos visuales directamente desde el código subyacente.
Desencadenadores de estado visual
Los desencadenadores de estado visual son un conjunto especializado de desencadenadores que definen las condiciones en las que se debe aplicar VisualState
.
Los desencadenadores de estado se agregan a la colección StateTriggers
de una clase VisualState
. Esta colección puede contener un único desencadenador de estado o varios desencadenadores de estado. Se aplicará una clase VisualState
cuando cualquier desencadenador de estado de la colección esté activo.
Al usar desencadenadores de estado para controlar los estados visuales, Xamarin.Forms usa las siguientes reglas de prioridad para determinar qué desencadenador, y la clase VisualState
correspondiente, se activará:
- Cualquier desencadenador derivado de
StateTriggerBase
. - Una clase
AdaptiveTrigger
activada debido a que se cumple la condiciónMinWindowWidth
. - Una clase
AdaptiveTrigger
activada debido a que se cumple la condiciónMinWindowHeight
.
Si hay varios desencadenadores activos simultáneamente (por ejemplo, dos desencadenadores personalizados), tiene prioridad el primer desencadenador declarado en el marcado.
Para obtener más información sobre desencadenadores de estado, vea Desencadenadores de estado.
Uso del Administrador de estado visual para el diseño adaptable
Una aplicación de Xamarin.Forms que se ejecuta en un teléfono normalmente se puede ver en una relación de aspecto vertical o horizontal, y un programa de Xamarin.Forms que se ejecuta en el escritorio se puede cambiar de tamaño para asumir muchos tamaños y relaciones de aspecto diferentes. Una aplicación bien diseñada podría mostrar su contenido de forma diferente para estos diversos factores de forma de página o ventana.
Esta técnica se conoce a veces como diseño adaptable. Dado que el diseño adaptable implica únicamente los objetos visuales de un programa, es una aplicación ideal del Administrador de estado visual.
Un ejemplo sencillo es una aplicación que muestra una pequeña colección de botones que afectan al contenido de la aplicación. En el modo vertical, estos botones se pueden mostrar en una fila horizontal en la parte superior de la página:
En el modo horizontal, es posible que la matriz de botones se mueva a un lado y se muestre en una columna:
De arriba a abajo, el programa se ejecuta en la Plataforma universal de Windows, Android e iOS.
La página Diseño adaptativo de VSM del ejemplo define un grupo denominado "OrientationStates" con dos estados visuales denominados "Vertical" y "Horizontal". (Un enfoque más complejo puede basarse en varios anchos de página o ventana diferentes).
El marcado de VSM se produce en cuatro lugares del archivo XAML. El objeto StackLayout
denominado mainStack
contiene tanto el menú como el contenido, que es un elemento Image
. StackLayout
debe tener una orientación vertical en modo vertical y una orientación horizontal en modo horizontal:
<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>
El elemento interno ScrollView
denominado menuScroll
y el StackLayout
con nombre menuStack
implementan el menú de botones. La orientación de estos diseños es opuesta a mainStack
. El menú debe ser horizontal en modo vertical y vertical en modo horizontal.
La cuarta sección del marcado VSM está en un estilo implícito para los propios botones. Este marcado establece las propiedades VerticalOptions
, HorizontalOptions
y Margin
específicas de las orientaciones verticales y horizontales.
El archivo de código subyacente establece la propiedad BindingContext
de menuStack
para implementar Button
en comandos y también adjunta un controlador al evento SizeChanged
de la página:
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; }
}
El controlador SizeChanged
llama VisualStateManager.GoToState
a los dos elementos StackLayout
y ScrollView
y, a continuación, recorre en bucle los elementos secundarios de menuStack
para llamar VisualStateManager.GoToState
a los elementos de Button
.
Puede parecer que el archivo de código subyacente puede controlar los cambios de orientación más directamente estableciendo propiedades de elementos en el archivo XAML, pero visual State Manager es definitivamente un enfoque más estructurado. Todos los objetos visuales se conservan en el archivo XAML, donde son más fáciles de examinar, mantener y modificar.
Administrador de estado visual con Xamarin.University
Xamarin.FormsVideo del Administrador de estado visual 3.0