Compartir a través de


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>
        <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>

En la captura de pantalla siguiente se muestra Entry en sus cuatro estados visuales definidos:

Captura de pantalla de los tres estados visuales definidos en la Entrada.

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:

Captura de pantalla del estado Presionado de un botón.

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:

Captura de pantalla del ejemplo de validación del estado visual.

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á:

  1. Cualquier desencadenador derivado de StateTriggerBase.
  2. Una clase AdaptiveTrigger activada debido a que se cumple la condición MinWindowWidth.
  3. 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.