Compartilhar via


Estados visuais

O Gerenciador de Estado Visual da interface do usuário do aplicativo .NET multiplataforma (.NET MAUI) fornece uma maneira estruturada de fazer alterações visuais na interface do usuário a partir do código. Na maioria dos casos, a interface do usuário de um aplicativo é definida em XAML e esse XAML pode incluir marcação que descreve como o Gerenciador de Estado Visual afeta os elementos visuais da interface do usuário.

O Gerenciador de Estado Visual apresenta o conceito de estados visuais. Uma exibição do .NET MAUI como um Button pode ter várias aparências visuais diferentes, dependendo de seu estado subjacente — se está desabilitado, pressionado ou tem foco de entrada. Estes são os estados do botão. Os estados visuais são coletados em grupos de estados visuais. Todos os estados visuais dentro de um grupo de estados visuais são mutuamente exclusivos. Os estados visuais e os grupos de estados visuais são identificados por cadeias de caracteres de texto simples.

O Gerenciador de Estado Visual do .NET MAUI define um grupo de estado visual chamado CommonStates com os seguintes estados visuais:

  • Normal
  • Desabilitado
  • Focalizado
  • Selecionado
  • PointerOver

Os estados visuais Normal, Disabled, Focused e PointerOver têm suporte em todas as classes derivadas de VisualElement, que é a classe base para View e Page. Além disso, você também pode definir seus próprios grupos de estado visual e estados visuais.

A vantagem de usar o Gerenciador de Estado Visual para definir a aparência, em vez de acessar elementos visuais diretamente do code-behind, é que você pode controlar como os elementos visuais reagem a um estado totalmente diferente no XAML, que mantém todo o design da interface do usuário em um único local.

Observação

Os gatilhos também podem fazer alterações em visuais na interface do usuário com base em alterações nas propriedades de um modo de exibição ou no acionamento de eventos. No entanto, usar gatilhos para lidar com várias combinações dessas mudanças pode se tornar confuso. Com o Gerenciador de Estado Visual, os estados visuais dentro de um grupo de estado visual são sempre mutuamente exclusivos. A qualquer momento, apenas um estado em cada grupo é o estado atual.

Estados visuais comuns

O Gerenciador de Estado Visual permite incluir marcação no arquivo XAML que pode alterar a aparência visual de um modo de exibição se o modo de exibição estiver normal, desabilitado, tiver foco de entrada, estiver selecionado ou estiver sendo focalizado pelo cursor, mas não pressionado. Estes são conhecidos como os estados comuns.

Por exemplo, suponha que você tenha um Entry modo de exibição em sua página e queira que a aparência visual do Entry mude das seguintes maneiras:

  • O Entry deve ter um fundo rosa quando o Entry está desabilitado.
  • O Entry deve ter um fundo limão normalmente.
  • O Entry deve expandir para o dobro de sua altura normal quando tem foco de entrada.
  • O Entry deve ter um plano de fundo azul claro quando o cursor do mouse estiver focalizando ele, mas não pressionando.

Você pode anexar a marcação do Gerenciador de Estado Visual a uma exibição individual ou defini-la em um estilo se ela se aplicar a várias exibições.

Definir estados visuais em um modo de exibição

A classe VisualStateManager define uma propriedade VisualStateGroups anexada, que é usada para anexar estados visuais a uma exibição. A propriedade VisualStateGroups é do tipo VisualStateGroupList, que é uma coleção de objetos VisualStateGroup. Portanto, o filho da propriedade VisualStateManager.VisualStateGroups anexada é um objeto VisualStateGroup. Esse objeto define um atributo x:Name que indica o nome do grupo. Como alternativa, a classe VisualStateGroup define uma propriedade Name que você pode usar. Para obter mais informações sobre propriedades anexadas, consulte Propriedades anexadas.

A classe VisualStateGroup define uma propriedade chamada States, que é uma coleção de objetos VisualState. States é a propriedade de conteúdo da classe VisualStateGroups para que você possa incluir os objetos VisualState como filhos do VisualStateGroup. Cada objeto VisualState deve ser identificado usando x:Name ou Name.

A classe VisualState define uma propriedade chamada Setters, que é uma coleção de objetos Setter. Esses são os mesmos objetos Setter que você usa em um objeto Style. Setters não é a propriedade de conteúdo de VisualState, portanto, é necessário incluir marcas de elemento de propriedade para a propriedade Setters. Objetos Setter devem ser inseridos como filhos de Setters. Cada objeto Setter indica o valor de uma propriedade quando esse estado é atual. Qualquer propriedade com referência a um objeto Setter deve ser apoiada por uma propriedade vinculável.

Importante

Para que os objetos Setter de estado visual objetos funcionem corretamente, um VisualStateGroup deve conter um objeto VisualState para o estado Normal. Se esse estado visual não tiver nenhum objeto Setter, ele deverá ser incluído como um estado visual vazio (<VisualState Name="Normal" />).

O exemplo a seguir mostra os estados visuais definidos em um 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>

A captura de tela a seguir mostra o Entry em seus quatro estados visuais definidos:

Captura de tela dos três estados visuais definidos na Entrada.

Quando o Entry está no estado Normal, sua tela de fundo é limão. Quando o Entry obtém o foco de entrada, o tamanho da fonte dobra. Quando o Entry fica desabilitado, o plano de fundo fica rosa. O Entry não mantém seu plano de fundo limão quando ganha foco de entrada. Quando o ponteiro do mouse passa o mouse sobre o Entry, mas não é pressionado, o plano de fundo Entry fica azul claro. Como o Gerenciador de Estado Visual alterna entre os estados visuais, as definições das propriedades definidas pelo estado anterior são removidas. Portanto, os estados visuais são mutuamente exclusivos.

Se você quiser que o Entry tenha um plano de fundo limão no estado Focused, adicione outro Setter a esse estado visual:

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

Definir estados visuais em um estilo

Geralmente, é necessário compartilhar os mesmos estados visuais em dois ou mais modos de exibição. Nesse cenário, os estados visuais podem ser definidos em um Style. Isso pode ser feito adicionando um objeto Setter à propriedade VisualStateManager.VisualStateGroups. A propriedade de conteúdo do objeto Setter é sua propriedade Value, que, portanto, pode ser especificada como o filho do objeto Setter. A propriedade VisualStateGroups é do tipo VisualStateGroupList e, portanto, o filho do objeto Setter é um VisualStateGroupList ao qual um VisualStateGroup pode ser adicionado que contém objetos VisualState.

O exemplo a seguir mostra um estilo implícito para um Entry que define os estados visuais comuns:

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

Quando esse estilo for incluído em um dicionário de recursos no nível da página, o objeto Style será aplicado a todos os objetos Entry na página. Portanto, todos os objetos Entry na página responderão da mesma forma aos seus estados visuais.

Estados visuais no .NET MAUI

A tabela a seguir lista os estados visuais definidos no .NET MAUI:

Classe Estados Mais informações
Button Pressed Estados visuais Button
CarouselView DefaultItem, CurrentItem, PreviousItem, NextItem Estados visuais CarouselView
CheckBox IsChecked Estados visuais CheckBox
CollectionView Selected Estados visuais CollectionView
ImageButton Pressed Estados visuais ImageButton
RadioButton Checked, Unchecked Estados visuais RadioButton
Switch On, Off Estados visuais Switch
VisualElement Normal, Disabled, Focused, PointerOver Estados comuns

Definir o estado em vários elementos

Nos exemplos anteriores, os estados visuais eram anexados e operados em elementos únicos. No entanto, também é possível criar estados visuais anexados a um único elemento, mas que definem propriedades em outros elementos dentro do mesmo escopo. Isso evita ter que repetir estados visuais em cada elemento em que os estados operam.

O tipo Setter tem uma propriedade TargetName do tipo string, que representa o objeto de destino que o Setter para um estado visual manipulará. Quando a propriedade TargetName é definida, o Setter define o Property do objeto definido em TargetName como Value:

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

Neste exemplo, um Label nomeado label terá sua propriedade TextColor definida como Red. Ao definir a propriedade TargetName, você deve especificar o caminho completo para a propriedade em Property. Portanto, para definir a propriedade TextColor em um Label, Property é especificado como Label.TextColor.

Observação

Qualquer propriedade com referência a um objeto Setter deve ser apoiada por uma propriedade vinculável.

O exemplo a seguir mostra como definir o estado em vários objetos, de um único grupo de estado visual:

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

Neste exemplo, o estado Normal está ativo quando o Button não é pressionado e uma resposta pode ser inserida no Entry. O estado Pressed torna-se ativo quando o Button é pressionado e especifica que sua propriedade Scale será alterada do valor padrão de 1 para 0,8. Além disso, o Entry nomeado como entry terá sua propriedade Text definida como Paris. Portanto, o resultado é que quando o Button é pressionado, ele é redimensionado para ser um pouco menor e Entry exibe Paris:

Captura de tela do estado Pressionado para um Botão.

Em seguida, quando o Button é liberado, ele é redimensionado para seu valor padrão de 1 e Entry exibe qualquer texto inserido anteriormente.

Importante

Os caminhos de propriedade não têm suporte em elementos Setter que especificam a propriedade TargetName.

Definir estados visuais personalizados

Os estados visuais personalizados podem ser implementados definindo-os como você definiria estados visuais para os estados comuns, mas com os nomes de sua escolha e chamando o método VisualStateManager.GoToState para ativar um estado.

O exemplo a seguir mostra como usar o Gerenciador de Estado Visual para validação 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>

Neste exemplo, os estados visuais são anexados ao StackLayout e há dois estados mutuamente exclusivos chamados Valid e Invalid. Se o Entry não contiver um número de telefone válido, o estado atual será Invalid e, portanto, o Entry terá um plano de fundo rosa, o segundo Label ficará visível e o Button será desabilitado. Quando um número de telefone válido é inserido, o estado atual se torna Valid. O Entry recebe um fundo limão, o segundo Label desaparece e o Button agora está habilitado:

Captura de tela do exemplo de validação de estado visual.

O arquivo code-behind é responsável por manipular o evento TextChanged do Entry. O manipulador usa uma expressão regular para determinar se a cadeia de caracteres de entrada é válida ou não. O método GoToState no arquivo code-behind chama o método VisualStateManager.GoToState estático no 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);
    }
}

Neste exemplo, o método GoToState é chamado do construtor para inicializar o estado. Deve haver sempre um estado atual. O arquivo code-behind chama VisualStateManager.GoToState, com um nome de estado, no objeto que define os estados visuais.

Gatilhos de estado visual

Os estados visuais oferecem suporte a gatilhos de estado, que são um grupo especializado de gatilhos que definem as condições sob as quais um VisualState deve ser aplicado.

Os gatilhos de estado são adicionados à coleção de StateTriggers de um VisualState. Essa coleção pode conter um ou vários gatilhos de estado. Um VisualState será aplicado quando qualquer gatilho de estado na coleção estiver ativo.

Ao usar gatilhos de estado para controlar estados visuais, o .NET MAUI usa as seguintes regras de precedência para determinar qual gatilho (e VisualStatecorrespondente) estará ativo:

  1. Um gatilho que deriva de StateTriggerBase.
  2. Um AdaptiveTrigger ativado porque a condição MinWindowWidth foi atendida.
  3. Um AdaptiveTrigger ativado porque a condição MinWindowHeight foi atendida.

Se múltiplos gatinhos estiverem simultaneamente ativos (por exemplo, dois gatilhos personalizados), o primeiro gatilho declarado na marcação terá precedência.

Para obter mais informações sobre gatilhos de estado, consulte Gatilhos de estado.