Estados visuales
El administrador de estado visual de .NET Multi-Platform App UI (.NET MAUI) 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 una aplicación se define en XAML y este XAML puede incluir marcado que describe cómo el administrador de estado visual afecta a los objetos visuales de la interfaz de usuario.
El administrador de estado visual introduce el concepto de estados visuales de . Una vista de .NET, MAUI como Button puede tener varias apariencias visuales diferentes en función de su estado subyacente, si está deshabilitada o pulsada 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 .NET MAUI define un grupo de estado visual denominado CommonStates
con los siguientes estados visuales:
- Normal
- Deshabilitado
- Enfocado
- Seleccionada
- PointerOver
Los estados visuales Normal
, Disabled
, Focused
y PointerOver
se admiten en todas las clases que derivan de VisualElement, que es la clase base para View y Page. Además, también puedes definir tus propios grupos de estados visuales y estados visuales.
La ventaja de usar el administrador de estado visual para definir la apariencia, en lugar de tener acceso a elementos visuales directamente desde el código subyacente, 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.
Nota:
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 confuso. Con el administrador de estado visual, 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.
Estados visuales comunes
El Administrador de estado visual te permite incluir marcado en el archivo XAML que puede cambiar la apariencia visual de una vista si la vista es normal, está deshabilitada, tiene el foco de entrada, está seleccionada o tiene el cursor del ratón sobre ella, pero no pulsado. 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 cuando Entry 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.
- Entry debe tener un fondo azul claro cuando tenga el cursor del ratón sobre él, pero no esté pulsado.
Puedes adjuntar el marcado del administrador de estado visual a una vista individual o puedes definirlo en un estilo si se aplica a varias vistas.
Definición de estados visuales en una vista
La clase VisualStateManager
define una propiedad adjunta VisualStateGroups
, que se usa para adjuntar estados visuales a una vista. La propiedad VisualStateGroups
es de tipo VisualStateGroupList
, que es una colección de objetos VisualStateGroup
. Por lo tanto, el elemento secundario de la propiedad VisualStateManager.VisualStateGroups
adjunta es un objeto VisualStateGroup
. Este objeto define un atributo x:Name
que indica el nombre del grupo. Como alternativa, la clase VisualStateGroup
define una propiedad Name
que puedes usar en su lugar. Para más información sobre las propiedades asociadas, consulta Propiedades asociadas.
La clase VisualStateGroup
también hereda una propiedad States
, que es una colección de objetos VisualState. States
es la propiedad de contenido de la clase VisualStateGroups
para que puedas incluir los objetos VisualState como elementos secundarios de VisualStateGroup
. Cada objeto VisualState debe identificarse mediante x:Name
o Name
.
La clase VisualState también hereda 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
. Los objetos Setter deben insertarse como elementos secundarios de Setters
. Cada objeto Setter indica el valor de una propiedad cuando ese estado es actual. Cualquier propiedad a la que hace referencia un objeto Setter debe estar respaldada por una propiedad enlazable.
Importante
Para que los objetos Setter de estado visual funcionen correctamente, un VisualStateGroup
debe contener un objeto VisualState para el estado Normal
. Si este estado visual no tiene ningún objeto Setter, debe incluirse como un estado visual vacío (<VisualState Name="Normal" />
).
En el ejemplo siguiente se muestran los estados visuales definidos en Entry:
<Entry FontSize="18">
<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" />
</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>
</VisualStateManager.VisualStateGroups>
</Entry>
En la captura de pantalla siguiente se muestra Entry en sus cuatro estados visuales definidos:
Cuando el objeto Entry está en estado Normal
, su fondo es cal. Cuando Entry obtiene el foco de entrada, su tamaño de fuente se duplica. Cuando Entry se deshabilita, su fondo se vuelve rosa. El Entry no conserva su fondo de cal cuando obtiene el foco de entrada. Cuando el puntero del mouse mantiene el puntero sobre Entry, pero no se presiona, el fondo de Entry se convierte en azul claro. A medida que el Administrador de estado visual cambia entre los estados visuales, las propiedades establecidas por el estado anterior no se establecen. Por lo tanto, los estados visuales son mutuamente excluyentes.
Si desea que Entry tenga un fondo de cal en el estado Focused
, agregue otro Setter a ese estado visual:
<VisualState Name="Focused">
<VisualState.Setters>
<Setter Property="FontSize" Value="36" />
<Setter Property="BackgroundColor" Value="Lime" />
</VisualState.Setters>
</VisualState>
Definir estados visuales en un estilo
A menudo es necesario compartir los mismos estados visuales en dos o más vistas. En este escenario, los estados visuales se pueden definir en Style Esto se puede lograr agregando un objeto Setter para la propiedad VisualStateManager.VisualStateGroups
. La propiedad de contenido del objeto Setter es su propiedad Value
, que se puede especificar como elemento secundario del objeto Setter. La propiedad de VisualStateGroups
es de tipo VisualStateGroupList
y, por tanto, el elemento secundario del objeto Setter es un objeto VisualStateGroupList
al que se puede agregar un objeto VisualStateGroup
que contiene objetos VisualState.
En el ejemplo siguiente se muestra un estilo implícito para un objeto Entry que define los estados visuales comunes:
<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>
Cuando este estilo se incluye en un diccionario de recursos de nivel de página, el objeto Style se aplicará a todos los objetos Entry de la página. Por lo tanto, todos los objetos Entry de la página responderán de la misma manera a sus estados visuales.
Estados visuales en .NET MAUI
En la tabla siguiente se enumeran los estados visuales definidos en .NET MAUI:
Clase | States | Más información |
---|---|---|
Button | Pressed |
Estados visuales del botón |
CarouselView | DefaultItem , CurrentItem , PreviousItem , NextItem |
Estados visuales de CarouselView |
CheckBox | IsChecked |
Estados visuales de CheckBox |
CollectionView | Selected |
Estados visuales de CollectionView |
ImageButton | Pressed |
Estados visuales de ImageButton |
RadioButton | Checked , Unchecked |
Estados visuales de RadioButton |
Switch | On , Off |
Cambiar estados visuales |
VisualElement | Normal , Disabled , Focused , PointerOver |
Pasos comunes |
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 objeto de destino que Setter para un estado visual manipulará. Cuando se define la propiedadTargetName
el objeto Setter establece el Property
del objeto definido en TargetName
a 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.
En el ejemplo siguiente se muestra cómo establecer el estado en varios objetos, desde un único grupo de estados visuales:
<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>
En este ejemplo, el estado Normal
está activo cuando Button no se presiona y se puede escribir una respuesta en Entry. El estado Pressed
se activa cuando Button se presiona y especifica que su propiedad Scale
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 Button se presiona, se vuelve a escalar para ser ligeramente más pequeño y 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 propiedad no se admiten en elementos Setter que especifican la propiedad TargetName
.
Definición de estados visuales personalizados
Los estados visuales personalizados se pueden implementar definiendo como definirías estados visuales para los estados comunes, pero con nombres de tu elección y luego llamando al método VisualStateManager.GoToState
para activar un estado.
En el ejemplo siguiente se muestra cómo usar Visual State Manager para la validación de entrada:
<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>
En este ejemplo, los estados visuales se adjuntan a StackLayout y hay dos estados mutuamente excluyentes denominados Valid
y Invalid
. Si Entry no contiene un número de teléfono válido, el estado actual es Invalid
y, por tanto Entry, tiene un fondo rosa, el segundo estado Label es visible y Button está deshabilitado. Cuando se escribe un número de teléfono válido, el estado actual se convierte en Valid
. 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 GoToState
del archivo de código subyacente llama al método estático VisualStateManager.GoToState
en el objeto 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);
}
}
En este ejemplo, el método GoToState
se llama desde el constructor para inicializar el estado. Siempre debe haber un estado actual. Después el archivo de código subyacente llama a VisualStateManager.GoToState
, con un nombre de estado, en el objeto que define los estados visuales.
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, .NET MAUI usa las siguientes reglas de prioridad para determinar qué desencadenador (y VisualState correspondiente) se activará:
- Cualquier desencadenador derivado de StateTriggerBase.
- Una clase AdaptiveTrigger activada debido a que se cumple la condición MinWindowWidth.
- Una clase AdaptiveTrigger activada debido a que se cumple la condición MinWindowHeight.
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.