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 Visual State Manager (VSM) 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 descrevendo como o Gerenciador de Estado Visual afeta os elementos visuais da interface do usuário.

O VSM introduz o conceito de estados visuais. Um Xamarin.Forms modo de exibição 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 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 de 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 elementos visuais na interface do usuário com base em alterações nas propriedades de um modo de exibição ou no disparo de eventos. No entanto, usar gatilhos para lidar com várias combinações dessas mudanças 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 estiver 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 um modo de exibição individual ou defini-lo em um estilo se ele se aplicar a vários modos de exibição. As duas seções seguintes descrevem essas abordagens.

Marcação VSM em um modo de exibição

Para anexar a marcação VSM a uma Entry exibição, primeiro separe as Entry marcas de início e fim:

<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 vinculá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 em vez disso:

<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 do 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 do , portanto, é necessário incluir marcas de elemento de VisualStatepropriedade 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 on View no programa de exemplo. A página inclui três Entry modos de exibição, mas apenas o segundo 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 desabilitado Entry 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 da UWP de não permite definir a cor do plano de Entry fundo quando o Entry está desabilitado.

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

VSM na exibição: Normal

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

VSM à vista: focado

Observe que o Entry não retém o fundo de cal quando recebe 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 está habilitado Entry e não tem foco de entrada.

Se você quiser que o Entry tenha um plano de fundo de cal 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, um VisualStateGroup deve conter VisualState objetos para todos os estados nesse grupo. Se houver um estado visual que não tenha nenhum Setter objeto, inclua-o de qualquer maneira como uma marca vazia:

<VisualState x:Name="Normal" />

Marcação do Gerenciador de Estado Visual em um estilo

Muitas vezes, é necessário compartilhar a mesma marcação do Visual State Manager entre dois ou mais modos de exibição. Nesse caso, convém 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 vinculável anexada:

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

    </Setter>
</Style>

A propriedade content for Setter é Value, portanto, o Value valor da 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 dos 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 de cal 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 Estado implementa CollectionViewSelected . 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 do elemento definido em TargetNameValue:

<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 contendo um Label elemento, 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.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 está anexada ao StackLayout. Existem dois estados mutuamente exclusivos, chamados "Normal" e "Pressionado", com cada estado contendo VisualState tags.

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

VSM Setter TargetName: Estado Normal

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

VSM Setter TargetName: 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 é pressionado é redimensionado Button para ser um pouco menor, e as Entry telas 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, os caminhos de propriedade não têm suporte nos Setter elementos que especificam a TargetName propriedade.

Defina seus próprios estados visuais

Cada classe que deriva de VisualElement suporta os estados comuns "Normal", "Focado" e "Desabilitado". Além disso, a CollectionView classe suporta o estado "Selected". Internamente, a VisualElement classe detecta quando está se tornando 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 que deriva do 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 para o Visual State Manager. Dentro de um dos dois programas de exemplo 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 é atual a qualquer momento.

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

A página Validação do VSM no exemplo mostra como usar o Gerenciador de Estado Visual 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 está anexada StackLayout ao (nomeado stackLayout). Há dois estados mutuamente exclusivos, chamados "Válido" e "Inválido", com cada estado contendo VisualState tags.

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 plano de fundo rosa, o segundo Label ficará visível e o Button será desabilitado:

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 têm como alvo 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 a configuração da 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 use 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.

Usar o Gerenciador de Estado Visual 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 de forma de página ou janela.

Essa técnica às vezes é conhecida como layout adaptativo. Como o layout adaptável envolve apenas os elementos visuais de um programa, ele é um aplicativo 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 "Portrait" e "Landscape". (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 à 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 BindingContext propriedade de menuStack para implementar Button o 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 dois StackLayout e ScrollView elementos e, em seguida, faz um loop pelos filhos de menuStack para chamar VisualStateManager.GoToState os Button elementos.

Pode parecer que o arquivo code-behind pode manipular 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 elementos visuais são mantidos no arquivo XAML, onde se tornam mais fáceis de examinar, manter e modificar.

Visual State Manager com Xamarin.University

Xamarin.Forms Vídeo do Gerenciador de Estado Visual 3.0