Compartilhar via


Xamarin.Forms Gerenciador de Estado Visual

Use o Gerenciador de Estado Visual para fazer alterações em elementos XAML com base em estados visuais definidos a partir do código.

O VSM (Visual State Manager) 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 do aplicativo é definida em XAML, e esse XAML inclui marcação que descreve como o Visual State Manager afeta os visuais da interface do usuário.

O VSM introduz o conceito de estados visuais. Uma Xamarin.Forms exibição como a Button pode ter várias aparências visuais diferentes, dependendo de seu estado subjacente — se está desabilitada, pressionada 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 Xamarin.Forms Visual State Manager define um grupo de estados visuais chamado "CommonStates" com os seguintes estados visuais:

  • "Normal"
  • "Desativado"
  • "Focado"
  • "Selecionado"

Esse grupo de estados visuais tem suporte para todas as classes derivadas de VisualElement, que é a classe base para View e Page.

Você também pode definir seus próprios grupos de estados visuais e estados visuais, como este artigo demonstrará.

Observação

Xamarin.Forms Os desenvolvedores familiarizados com gatilhos estão cientes de que os gatilhos também podem fazer alterações nos visuais na interface do usuário com base nas alterações nas propriedades de uma exibição ou no disparo de eventos. No entanto, usar gatilhos para lidar com várias combinações dessas alterações pode se tornar bastante confuso. Historicamente, o Visual State Manager foi introduzido em ambientes baseados em XAML do Windows para aliviar a confusão resultante de combinações de estados visuais. Com o VSM, os estados visuais dentro de um grupo de estados visuais são sempre mutuamente exclusivos. A qualquer momento, apenas um estado em cada grupo é o estado atual.

Estados comuns

O Gerenciador de Estado Visual permite que você inclua marcação em seu arquivo XAML que pode alterar a aparência visual de um modo de exibição se o modo de exibição for normal, desabilitado ou tiver o foco de entrada. 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.

Você pode anexar a marcação VSM a uma exibição individual ou pode defini-la em um estilo se ela se aplicar a várias exibições. As próximas duas seções descrevem essas abordagens.

Marcação VSM em uma exibição

Para anexar a marcação VSM a uma Entry exibição, primeiro separe as Entry tags inicial e final:

<Entry FontSize="18">

</Entry>

Ele recebe um tamanho de fonte explícito porque um dos estados usará a FontSize propriedade para dobrar o tamanho do texto no Entry.

Em seguida, insira VisualStateManager.VisualStateGroups tags entre essas tags:

<Entry FontSize="18">
    <VisualStateManager.VisualStateGroups>

    </VisualStateManager.VisualStateGroups>
</Entry>

VisualStateGroups é uma propriedade associável anexada VisualStateManager definida pela classe. (Para obter mais informações sobre propriedades vinculáveis anexadas, consulte o artigo Propriedades anexadas.) É assim que a VisualStateGroups propriedade é anexada Entry ao objeto.

A propriedade VisualStateGroups é do tipo VisualStateGroupList, que é uma coleção de objetos VisualStateGroup. Dentro das VisualStateManager.VisualStateGroups tags, insira um par de VisualStateGroup tags para cada grupo de estados visuais que você deseja incluir:

<Entry FontSize="18">
    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup x:Name="CommonStates">

        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>
</Entry>

Observe que a VisualStateGroup tag tem um x:Name atributo indicando o nome do grupo. A VisualStateGroup classe define uma Name propriedade que você pode usar:

<VisualStateGroup Name="CommonStates">

Você pode usar um x:Name ou Name mas não ambos no mesmo elemento.

A classe VisualStateGroup define uma propriedade chamada States, que é uma coleção de objetos VisualState. States é a propriedade content de VisualStateGroups para que você possa incluir as VisualState tags diretamente entre as VisualStateGroup tags. (As propriedades de conteúdo são discutidas no artigo Sintaxe XAML essencial.)

A próxima etapa é incluir um par de tags para cada estado visual nesse grupo. Estes também podem ser identificados usando x:Name ou 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 uma propriedade chamada Setters, que é uma coleção de Setter objetos. Esses são os mesmos objetos Setter que você usa em um objeto Style.

Settersnão é a propriedade content de , portanto, é necessário incluir tags de VisualStateelemento de propriedade para a Setters propriedade:

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

Agora você pode inserir um ou mais Setter objetos entre cada par de Setters tags. Estes são os Setter objetos que definem os estados visuais 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 Setter tag indica o valor de uma propriedade específica quando esse estado é atual. Qualquer propriedade com referência a um objeto Setter deve ser apoiada por uma propriedade vinculável.

Marcação semelhante a essa é a base da página VSM na exibição no programa de exemplo. A página inclui três Entry visualizações, mas apenas a segunda tem a marcação VSM anexada a ela:

<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 o segundo Entry também tem um DataTrigger como parte de sua Trigger coleção. Isso faz com que o seja Entry desativado até que algo seja digitado no terceiro Entry. Aqui está a página na inicialização em execução no iOS, Android e na Plataforma Universal do Windows (UWP):

VSM na exibição: Desativado

O estado visual atual é "Desativado", portanto, o plano de fundo do segundo Entry é rosa nas telas do iOS e do Android. A implementação UWP de não permite definir a cor da tela de Entry fundo quando o Entry está desabilitado.

Quando você insere algum texto no terceiro Entry, o segundo Entry muda para o estado "Normal" e o plano de fundo agora é limão:

VSM na exibição: Normal

Quando você toca no segundo Entry, ele obtém o foco de entrada. Ele muda para o estado "Focado" e se expande para o dobro de sua altura:

VSM em exibição: Focado

Observe que o Entry não retém o fundo de cal quando obtém o foco de entrada. Como o Gerenciador de Estado Visual alterna entre os estados visuais, as definições das propriedades definidas pelo estado anterior são removidas. Lembre-se de que os estados visuais são mutuamente exclusivos. O estado "Normal" não significa apenas que o Entry está habilitado. Isso significa que o Entry está habilitado e não tem foco de entrada.

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

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

Para que esses Setter objetos funcionem corretamente, a VisualStateGroup deve conter VisualState objetos para todos os estados desse grupo. Se houver um estado visual que não tenha nenhum Setter objeto, inclua-o de qualquer maneira como uma tag vazia:

<VisualState x:Name="Normal" />

Marcação do Visual State Manager em um estilo

Geralmente, é necessário compartilhar a mesma marcação do Visual State Manager entre duas ou mais exibições. Nesse caso, você desejará colocar a marcação em uma Style definição.

Aqui está o implícito Style existente para os Entry elementos na página VSM On View :

<Style TargetType="Entry">
    <Setter Property="Margin" Value="20, 0" />
    <Setter Property="FontSize" Value="18" />
</Style>

Adicione Setter tags para a VisualStateManager.VisualStateGroups propriedade associável anexada:

<Style TargetType="Entry">
    <Setter Property="Margin" Value="20, 0" />
    <Setter Property="FontSize" Value="18" />
    <Setter Property="VisualStateManager.VisualStateGroups">

    </Setter>
</Style>

A propriedade de conteúdo para Setter é Value, portanto, o valor da Value propriedade pode ser especificado diretamente nessas tags. Essa propriedade é do 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 dessas tags, você pode incluir um ou mais VisualStateGroup objetos:

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

O restante da marcação VSM é o mesmo de antes.

Aqui está a página VSM em estilo mostrando a marcação VSM completa:

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

Agora, todas as Entry visualizações nesta página respondem da mesma maneira aos seus estados visuais. Observe também que o estado "Focado" agora inclui um segundo Setter que dá a cada Entry um um fundo limão também quando tem foco de entrada:

VSM em grande estilo

Estados visuais em Xamarin.Forms

A tabela a seguir lista os estados visuais definidos em Xamarin.Forms:

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

Cada um desses estados pode ser acessado por meio do grupo de estados visuais chamado CommonStates.

Além disso, o implementa CollectionView o Selected estado. Para obter mais informações, consulte Alterar a cor do item selecionado.

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 Setter tipo tem uma TargetName propriedade, do tipo string, que representa o elemento de destino que o Setter para um estado visual manipulará. Quando a TargetName propriedade é definida, o Setter define o Property elemento 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.

A página VSM com Setter TargetName no exemplo mostra como definir o estado em vários elementos, a partir de um único grupo de estados visuais. O arquivo XAML consiste em um StackLayout elemento que contém Label , um Entrye um 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>

A marcação VSM é anexada StackLayoutao . Há dois estados mutuamente exclusivos, chamados "Normal" e "Pressionado", com cada estado contendo VisualState tags.

O estado "Normal" está ativo quando o Button não é pressionado e uma resposta à pergunta pode ser inserida:

VSM Setter TargetName: Estado Normal

O estado "Pressionado" torna-se ativo quando o Button é pressionado:

Nome do destino do setter VSM: estado pressionado

O "Pressionado" VisualState especifica que, quando o Button é pressionado, sua Scale propriedade 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 o Entry exibe Paris. Em seguida, quando o Button é liberado, ele é redimensionado para seu valor padrão de 1 e Entry exibe qualquer texto inserido anteriormente.

Importante

No momento, não há suporte para caminhos de propriedade em Setter elementos que especificam a TargetName propriedade.

Defina seus próprios estados visuais

Cada classe derivada de VisualElement suporta os estados comuns "Normal", "Focado" e "Desabilitado". Além disso, a CollectionView classe dá suporte ao estado "Selected". Internamente, a classe detecta VisualElement quando está sendo habilitada ou desabilitada, ou focada ou desfocada, e chama o método estático VisualStateManager.GoToState :

VisualStateManager.GoToState(this, "Focused");

Esse é o único código do Visual State Manager que você encontrará na VisualElement classe. Como GoToState é chamado para cada objeto com base em cada classe derivada de VisualElement, você pode usar o Visual State Manager com qualquer VisualElement objeto para responder a essas alterações.

Curiosamente, o nome do grupo de estados visuais "CommonStates" não é explicitamente referenciado no VisualElement. O nome do grupo não faz parte da API do Visual State Manager. Em um dos dois programas de amostra mostrados até agora, você pode alterar o nome do grupo de "CommonStates" para qualquer outra coisa, e o programa ainda funcionará. O nome do grupo é apenas uma descrição geral dos estados desse grupo. Entende-se implicitamente que os estados visuais em qualquer grupo são mutuamente exclusivos: um estado e apenas um estado são atuais a qualquer momento.

Se você quiser implementar seus próprios estados visuais, precisará chamar VisualStateManager.GoToState do código. Na maioria das vezes, você fará essa chamada do arquivo code-behind da sua classe de página.

A página Validação do VSM no exemplo mostra como usar o Visual State Manager em conexão com a validação de entrada. O arquivo XAML consiste em um StackLayout contendo dois Label elementos, um Entry, e um 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>

A marcação VSM é anexada StackLayout ao (nomeado stackLayout). Há dois estados mutuamente exclusivos, chamados "Válido" e "Inválido", com cada estado contendo VisualState marcas.

Se o Entry não contiver um número de telefone válido, o estado atual será "Inválido" e, portanto, o Entry terá um fundo rosa, o segundo Label ficará visível e o Button será desativado:

Validação do VSM: estado inválido

Quando um número de telefone válido é inserido, o estado atual se torna "Válido". O Entry recebe um fundo limão, o segundo Label desaparece e o Button agora está habilitado:

Validação do VSM: estado válido

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 no arquivo code-behind chamado GoToState chama o 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 também que o GoToState método é chamado do construtor para inicializar o estado. Deve haver sempre um estado atual. Mas em nenhum lugar do código há qualquer referência ao nome do grupo de estados visuais, embora ele seja referenciado no XAML como "ValidationStates" para fins de clareza.

Observe que o arquivo code-behind só precisa levar em conta o objeto na página que define os estados visuais e chamar VisualStateManager.GoToState esse objeto. Isso ocorre porque ambos os estados visuais são direcionados a vários objetos na página.

Você pode se perguntar: se o arquivo code-behind deve fazer referência ao objeto na página que define os estados visuais, por que o arquivo code-behind não pode simplesmente acessar esse e outros objetos diretamente? Certamente poderia. No entanto, a vantagem de usar o VSM é que você pode controlar como os elementos visuais reagem a diferentes estados inteiramente em XAML, o que mantém todo o design da interface do usuário em um único local. Isso evita definir a aparência visual acessando elementos visuais diretamente do code-behind.

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, Xamarin.Forms usa as seguintes regras de precedência para determinar qual gatilho (e correspondente VisualState) 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.

Usar o Visual State Manager para layout adaptável

Um Xamarin.Forms aplicativo em execução em um telefone geralmente pode ser visualizado em uma proporção retrato ou paisagem, e um Xamarin.Forms programa em execução na área de trabalho pode ser redimensionado para assumir muitos tamanhos e proporções diferentes. Um aplicativo bem projetado pode exibir seu conteúdo de forma diferente para esses vários fatores forma de página ou janela.

Essa técnica às vezes é conhecida como layout adaptável. Como o layout adaptável envolve apenas os visuais de um programa, ele é uma aplicação ideal do Visual State Manager.

Um exemplo simples é um aplicativo que exibe uma pequena coleção de botões que afetam o conteúdo do aplicativo. No modo retrato, esses botões podem ser exibidos em uma linha horizontal na parte superior da página:

Layout adaptável do VSM: Retrato

No modo paisagem, a matriz de botões pode ser movida para um lado e exibida em uma coluna:

Layout adaptável do VSM: paisagem

De cima para baixo, o programa está sendo executado na Plataforma Universal do Windows, Android e iOS.

A página Layout Adaptável do VSM no exemplo define um grupo chamado "OrientationStates" com dois estados visuais chamados "Retrato" e "Paisagem". (Uma abordagem mais complexa pode ser baseada em várias larguras de página ou janela diferentes.)

A marcação VSM ocorre em quatro locais no arquivo XAML. O StackLayout nomeado mainStack contém o menu e o conteúdo, que é um Image elemento. Isso StackLayout deve ter uma orientação vertical no modo retrato e uma orientação horizontal no modo paisagem:

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

O nome menuScroll interno ScrollView e o StackLayout nomeado menuStack implementam o menu de botões. A orientação desses layouts é oposta a mainStack. O menu deve ser horizontal no modo retrato e vertical no modo paisagem.

A quarta seção da marcação VSM está em um estilo implícito para os próprios botões. Essa marcação define VerticalOptions, HorizontalOptionse Margin propriedades específicas para as orientações retrato e paisagem.

O arquivo code-behind define a propriedade de menuStack para implementar Button o BindingContext comando e também anexa um manipulador ao SizeChanged evento da 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; }
}

O SizeChanged manipulador chama VisualStateManager.GoToState os elementos two StackLayout and ScrollView e, em seguida, percorre os filhos de menuStack para chamar VisualStateManager.GoToState os Button elementos.

Pode parecer que o arquivo code-behind pode lidar com alterações de orientação mais diretamente definindo propriedades de elementos no arquivo XAML, mas o Visual State Manager é definitivamente uma abordagem mais estruturada. Todos os visuais são mantidos no arquivo XAML, onde se tornam mais fáceis de examinar, manter e modificar.

Gerenciador de Estado Visual com Xamarin.University

Xamarin.Forms 3.0 Vídeo do Visual State Manager