Layouts dinâmicos com XAML

O sistema de layout XAML fornece dimensionamento automático de elementos, painéis de layout e estados visuais para ajudar você a criar uma interface do usuário dinâmica. Com um layout dinâmico, é possível aprimorar a aparência do aplicativo em telas com diferentes tamanhos, resoluções, densidades de pixel e orientações de janela. Também é possível usar o XAML para reposicionar, redimensionar, refluir, mostrar/ocultar, substituir e reformular a interface do usuário do aplicativo, conforme discutido em Técnicas de design dinâmicas. Discutiremos aqui como implementar layouts dinâmicos com o XAML.

Layouts fluidos com propriedades e painéis

A base de um layout dinâmico é o uso apropriado de propriedades e painéis de layout XAML para reposicionar, redimensionar e refluir o conteúdo de maneira fluida.

O sistema de layout XAML dá suporte a layouts estáticos e fluidos. Em um layout estático, você fornece aos controles tamanhos e posições de pixel explícitos. Quando o usuário altera a resolução ou a orientação do dispositivo, a interface do usuário não é alterada. Os layouts estáticos podem ser recortados em diferentes fatores forma e tamanhos de exibição. Por outro lado, os layouts fluidos reduzem, crescem e refluem para responderem ao espaço visual disponível em um dispositivo.

Na prática, você usa uma combinação de elementos estáticos e fluidos para criar sua interface do usuário. Você ainda usa elementos e valores estáticos em alguns lugares, mas verifique se a interface do usuário geral responde a diferentes resoluções, tamanhos de tela e modos de exibição.

Discutiremos aqui como usar painéis de layout e propriedades XAML para criar um layout fluido.

Propriedades de layout

As propriedade de layout controlam o tamanho e a posição de um elemento. Para criar um layout fluido, use o dimensionamento automático ou proporcional de elementos e permita que os painéis de layout posicionem seus filhos conforme necessário.

Aqui estão algumas propriedades de layout comuns e como usá-las para criar layouts fluidos.

Height e Width

As propriedades Height e Width especificam o tamanho de um elemento. Você pode usar valores fixos medidos em pixels efetivos ou pode usar o dimensionamento automático ou proporcional.

O dimensionamento automático redimensiona os elementos de interface do usuário para caber no contêiner de conteúdo ou pai. Você também pode usar o dimensionamento automático com as linhas e colunas de uma grade. Para usar o dimensionamento automático, defina a Altura e/ou Largura dos elementos da interface do usuário como Automático.

Observação

O redimensionamento de um elemento para seu conteúdo ou seu contêiner depende de como o contêiner pai manipula o dimensionamento dos filhos. Para obter mais informações, confira Painéis de layout posteriormente neste artigo.

O dimensionamento proporcional, também chamado de dimensionamento em estrela, distribui o espaço disponível entre as linhas e as colunas de uma grade segundo proporções ponderadas. Em XAML, os valores de estrela são expressos como * (ou n* para dimensionamento de estrela ponderado). Por exemplo, para especificar que uma coluna é 5 vezes mais larga que a segunda coluna em um layout de 2 colunas, use "5*" e "*" para as propriedades Width nos elementos ColumnDefinition.

Este exemplo combina dimensionamento fixo, automático e proporcional em uma Grid com 4 colunas.

Coluna Dimensionamento Descrição
Column_1 Auto A coluna será dimensionada para caber em seu conteúdo.
Column_2 * Depois que as colunas Auto são calculadas, a coluna obtém parte da largura restante. Column_2 terá metade da largura de Column_4.
Column_3 44 A coluna terá 44 pixels de largura.
Column_4 2* Depois que as colunas Auto são calculadas, a coluna obtém parte da largura restante. Column_4 será duas vezes mais largo que Column_2.

A largura padrão da coluna é "*", portanto, você não precisa definir explicitamente esse valor para a segunda coluna.

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition/>
        <ColumnDefinition Width="44"/>
        <ColumnDefinition Width="2*"/>
    </Grid.ColumnDefinitions>
    <TextBlock Text="Column 1 sizes to its content." FontSize="24"/>
</Grid>

No designer XAML do Visual Studio, o resultado tem esta aparência.

Uma grade de 4 colunas no designer do Visual Studio

Para obter o tamanho de um elemento em runtime, use as propriedades somente leitura ActualHeight e ActualWidth, em vez de Height e Width.

Restrições de tamanho

Ao usar o dimensionamento automático em sua interface do usuário, talvez ainda seja necessário colocar restrições no tamanho de um elemento. Você pode definir as propriedades MinWidth/MaxWidth e MinHeight/MaxHeight para especificar valores que restringem o tamanho de um elemento enquanto permitem o redimensionamento fluido.

Em uma Grid, MinWidth/MaxWidth também pode ser usado com definições de coluna e MinHeight/MaxHeight pode ser usado com definições de linha.

Alinhamento

Use as propriedades HorizontalAlignment e VerticalAlignment para especificar como um elemento deve ser posicionado em seu contêiner pai.

  • Os valores de HorizontalAlignment são Left, Center, Right e Stretch.
  • Os valores de VerticalAlignment são Top, Center, Bottom e Stretch.

Com o alinhamento Stretch , os elementos preenchem todo o espaço fornecido no contêiner pai. Esticar é o padrão para ambas as propriedades de alinhamento. No entanto, alguns controles, como Button, substituem esse valor em seu estilo padrão. Qualquer elemento que possa ter elementos filho pode tratar o valor Stretch para as propriedades HorizontalAlignment e VerticalAlignment exclusivamente. Por exemplo, um elemento que usa os valores padrão de Stretch colocados em uma Grid se estende para preencher a célula que o contém. O mesmo elemento colocado em um Canvas dimensiona para seu conteúdo. Para obter mais informações sobre como cada painel lida com o valor Stretch, consulte o artigo Painéis de layout.

Para obter mais informações, consulte o artigo Alinhamento, margem e preenchimento e as páginas de referência HorizontalAlignment e VerticalAlignment.

Visibilidade

Você pode revelar ou ocultar um elemento definindo sua propriedade Visibility como um dos valores de enumeração Visibility: Visible ou Collapsed. Quando um elemento é recolhido, ele não ocupa espaço no layout da interface do usuário.

Você pode alterar a propriedade Visibility de um elemento no código ou em um estado visual. Quando a Visibilidade de um elemento é alterada, todos os seus elementos filho também são alterados. Você pode substituir seções da interface do usuário revelando um painel enquanto recolhe outro.

Dica

Quando você tiver elementos em sua interface do usuário Collapsed por padrão, ainda assim os objetos serão criados na inicialização, mesmo quando não estiverem visíveis. Você pode adiar o carregamento desses elementos até que eles sejam mostrados usando o atributo x:Load para atrasar a criação dos objetos. Isso pode melhorar o desempenho da inicialização. Para obter mais informações, consulte o atributo x:Load.

Recursos de estilo

Você não precisa definir cada valor de propriedade individualmente em um controle. Normalmente, é mais eficiente agrupar valores de propriedade em um recurso Style e aplicar o Style a um controle. Isso é especialmente verdadeiro quando você precisa aplicar os mesmos valores de propriedade a muitos controles. Para obter mais informações sobre como usar estilos, consulte Controles de estilo.

Painéis de layout

Para posicionar objetos visuais, você deve colocá-los em um painel ou outro objeto de contêiner. A estrutura XAML fornece várias classes de painel, como Canvas, Grid, RelativePanel e StackPanel, que servem como contêineres e permitem que você posicione e organize os elementos da interface do usuário dentro deles.

A principal coisa a considerar ao escolher um painel de layout é como o painel posiciona e dimensiona seus elementos filhos. Você também pode precisar considerar como os elementos filho sobrepostos são colocados em camadas uns sobre os outros.

Aqui está uma comparação dos principais recursos dos controles de painel fornecidos na estrutura XAML.

Controle de painel Descrição
Tela O Canvas não oferece suporte à interface do usuário fluida; você controla todos os aspectos do posicionamento e dimensionamento de elementos filho. Normalmente, você o usa para casos especiais, como criar gráficos ou definir pequenas áreas estáticas de uma interface do usuário adaptável maior. Você pode usar código ou estados visuais para reposicionar elementos em tempo de execução.
  • Os elementos são posicionados absolutamente usando as propriedades anexadas Canvas.Top e Canvas.Left.
  • As camadas podem ser especificadas explicitamente usando a propriedade anexada Canvas.ZIndex.
  • Os valores de alongamento para HorizontalAlignment/VerticalAlignment são ignorados. Se o tamanho de um elemento não for definido explicitamente, ele será dimensionado de acordo com seu conteúdo.
  • O conteúdo filho não é recortado visualmente se for maior que o painel.
  • O conteúdo filho não é restrito pelos limites do painel.
  • Grid O Grid suporta o redimensionamento fluido de elementos filho. Você pode usar código ou estados visuais para reposicionar e refluir elementos.
  • Os elementos são organizados em linhas e colunas usando as propriedades anexadas Grid.Row e Grid.Column.
  • Os elementos podem abranger várias linhas e colunas usando as propriedades anexadas Grid.RowSpan e Grid.ColumnSpan.
  • Os valores de alongamento para HorizontalAlignment/VerticalAlignment são respeitados. Se o tamanho de um elemento não for definido explicitamente, ele se estende para preencher o espaço disponível na célula da grade.
  • O conteúdo filho é recortado visualmente se for maior que o painel.
  • O tamanho do conteúdo é restrito pelos limites do painel, portanto, o conteúdo rolável mostra barras de rolagem, se necessário.
  • RelativePanel
  • Os elementos são organizados em relação à aresta ou ao centro do painel e em relação uns aos outros.
  • Os elementos são posicionados usando uma variedade de propriedades anexadas que controlam o alinhamento do painel, o alinhamento dos irmãos e a posição dos irmãos.
  • Os valores de alongamento para HorizontalAlignment/VerticalAlignment são ignorados, a menos que as propriedades anexadas de RelativePanel para alinhamento causem alongamento (por exemplo, um elemento é alinhado às bordas direita e esquerda do painel). Se o tamanho de um elemento não for definido explicitamente e não for esticado, ele será dimensionado de acordo com seu conteúdo.
  • O conteúdo filho é recortado visualmente se for maior que o painel.
  • O tamanho do conteúdo é restrito pelos limites do painel, portanto, o conteúdo rolável mostra barras de rolagem, se necessário.
  • StackPanel
  • Os elementos são empilhados em uma única linha, vertical ou horizontalmente.
  • Os valores de alongamento para HorizontalAlignment/VerticalAlignment são respeitados na direção oposta à propriedade Orientation. Se o tamanho de um elemento não estiver definido explicitamente, ele será esticado para preencher a largura disponível (ou altura se a Orientação for Horizontal). Na direção especificada pela propriedade Orientation, um elemento é dimensionado de acordo com seu conteúdo.
  • O conteúdo filho é recortado visualmente se for maior que o painel.
  • O tamanho do conteúdo não é restrito pelos limites do painel na direção especificada pela propriedade Orientation, portanto, o conteúdo rolável se estende além dos limites do painel e não mostra barras de rolagem. Você deve restringir explicitamente a altura (ou largura) do conteúdo filho para fazer com que suas barras de rolagem sejam exibidas.
  • VariableSizedWrapGrid
  • Os elementos são organizados em linhas ou colunas que são encapsuladas automaticamente em uma nova linha ou coluna quando o valor MaximumRowsOrColumns é atingido.
  • Se os elementos são organizados em linhas ou colunas é especificado pela propriedade Orientation.
  • Os elementos podem abranger várias linhas e colunas usando as propriedades anexadas VariableSizedWrapGrid.RowSpan e VariableSizedWrapGrid.ColumnSpan.
  • Os valores de alongamento para HorizontalAlignment e VerticalAlignment são ignorados. Os elementos são dimensionados conforme especificado pelas propriedades ItemHeight e ItemWidth . Se essas propriedades não forem definidas, elas usarão os valores do tamanho da primeira célula.
  • O conteúdo filho é recortado visualmente se for maior que o painel.
  • O tamanho do conteúdo é restrito pelos limites do painel, portanto, o conteúdo rolável mostra barras de rolagem, se necessário.
  • Para obter informações detalhadas e exemplos desses painéis, consulte Painéis de layout.

    Os painéis de layout permitem que você organize sua interface do usuário em grupos lógicos de controles. Ao usá-los com as configurações de propriedade apropriadas, você obtém algum suporte para redimensionamento, reposicionamento e refluxo automáticos de elementos da interface do usuário. No entanto, a maioria dos layouts de interface do usuário precisa de modificações adicionais quando há alterações significativas no tamanho da janela. Para isso, você pode usar estados visuais.

    Layouts adaptáveis com estados visuais e gatilhos de estado

    Use estados visuais para fazer alterações significativas na interface do usuário com base no tamanho da janela ou em outras alterações.

    Quando a janela do aplicativo aumenta ou diminui além de um determinado valor, talvez você queira alterar as propriedades de layout para reposicionar, redimensionar, refluir, revelar ou substituir seções da interface do usuário. Você pode definir diferentes estados visuais para sua interface do usuário e aplicá-los quando a largura ou a altura da janela ultrapassar um limite especificado.

    Um VisualState define valores de propriedade que são aplicados a um elemento quando ele está em um estado específico. Você agrupa estados visuais em um VisualStateManager que aplica o VisualState apropriado quando as condições especificadas são atendidas. Um AdaptiveTrigger oferece uma forma fácil de definir o limite (também chamado de 'ponto de interrupção'), em que um estado é aplicado em XAML. Ou, então, você pode chamar o método VisualStateManager.GoToState no código para aplicar o estado visual. São mostrados exemplos das duas maneiras nas próximas seções.

    Definir estados visuais no código

    Para aplicar um estado visual do código, chame o método VisualStateManager.GoToState. Por exemplo, para aplicar um estado quando a janela do aplicativo tiver um tamanho específico, manipule o evento SizeChanged e chame GoToState para aplicar o estado apropriado.

    Aqui, um VisualStateGroup contém duas definições VisualState. O primeiro, DefaultState, está vazio. Quando ele é aplicado, os valores definidos na página XAML são aplicados. O segundo, WideState, altera a propriedade DisplayMode do SplitView para Inline e abre o painel. Esse estado é aplicado no manipulador de eventos SizeChanged caso a largura da janela seja superior a 640 pixels efetivos.

    Observação

    O Windows não permite que o aplicativo detecte o dispositivo em que o aplicativo está sendo executado. Ele pode informar a família de dispositivos (desktop, etc.) em que o aplicativo está sendo executado, a resolução efetiva e a quantidade de espaço na tela disponível para o aplicativo (o tamanho da janela do aplicativo). É recomendável definir estados visuais para tamanhos de tela e pontos de interrupção.

    <Page ...
        SizeChanged="CurrentWindow_SizeChanged">
        <Grid>
            <VisualStateManager.VisualStateGroups>
                <VisualStateGroup>
                    <VisualState x:Name="DefaultState">
                            <Storyboard>
                            </Storyboard>
                        </VisualState>
    
                    <VisualState x:Name="WideState">
                        <Storyboard>
                            <ObjectAnimationUsingKeyFrames
                                Storyboard.TargetProperty="SplitView.DisplayMode"
                                Storyboard.TargetName="mySplitView">
                                <DiscreteObjectKeyFrame KeyTime="0">
                                    <DiscreteObjectKeyFrame.Value>
                                        <SplitViewDisplayMode>Inline</SplitViewDisplayMode>
                                    </DiscreteObjectKeyFrame.Value>
                                </DiscreteObjectKeyFrame>
                            </ObjectAnimationUsingKeyFrames>
                            <ObjectAnimationUsingKeyFrames
                                Storyboard.TargetProperty="SplitView.IsPaneOpen"
                                Storyboard.TargetName="mySplitView">
                                <DiscreteObjectKeyFrame KeyTime="0" Value="True"/>
                            </ObjectAnimationUsingKeyFrames>
                        </Storyboard>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>
    
            <SplitView x:Name="mySplitView" DisplayMode="CompactInline"
                       IsPaneOpen="False" CompactPaneLength="20">
                <!-- SplitView content -->
    
                <SplitView.Pane>
                    <!-- Pane content -->
                </SplitView.Pane>
            </SplitView>
        </Grid>
    </Page>
    
    private void CurrentWindow_SizeChanged(object sender, Windows.UI.Core.WindowSizeChangedEventArgs e)
    {
        if (e.Size.Width > 640)
            VisualStateManager.GoToState(this, "WideState", false);
        else
            VisualStateManager.GoToState(this, "DefaultState", false);
    }
    
    // YourPage.h
    void CurrentWindow_SizeChanged(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::SizeChangedEventArgs const& e);
    
    // YourPage.cpp
    void YourPage::CurrentWindow_SizeChanged(IInspectable const& sender, SizeChangedEventArgs const& e)
    {
        if (e.NewSize.Width > 640)
            VisualStateManager::GoToState(*this, "WideState", false);
        else
            VisualStateManager::GoToState(*this, "DefaultState", false);
    }
    
    

    Definir estados visuais na marcação XAML

    Antes do Windows 10, as definições do VisualState exigiam objetos Storyboard para alterações de propriedade, e você precisava chamar GoToState no código para aplicar o estado. Isso é mostrado no exemplo anterior. Você ainda verá muitos exemplos que usam essa sintaxe ou pode ter um código existente que a use.

    A partir do Windows 10, você pode usar a sintaxe Setter simplificada mostrada aqui e pode usar um StateTrigger em sua marcação XAML para aplicar o estado. Você usa gatilhos de estado para criar regras simples que disparam automaticamente alterações de estado visual em resposta a um evento de aplicativo.

    Este exemplo faz a mesma coisa que o exemplo anterior, mas usa a sintaxe Setter simplificada em vez de um Storyboard para definir alterações de propriedade. E em vez de chamar GoToState, ele usa o gatilho de estado AdaptiveTrigger interno para aplicar o estado. Ao usar acionadores de estado, você não precisa definir um .DefaultState As configurações padrão são reaplicadas automaticamente quando as condições do gatilho de estado não são mais atendidas.

    <Page ...>
        <Grid>
            <VisualStateManager.VisualStateGroups>
                <VisualStateGroup>
                    <VisualState>
                        <VisualState.StateTriggers>
                            <!-- VisualState to be triggered when the
                                 window width is >=640 effective pixels. -->
                            <AdaptiveTrigger MinWindowWidth="640" />
                        </VisualState.StateTriggers>
    
                        <VisualState.Setters>
                            <Setter Target="mySplitView.DisplayMode" Value="Inline"/>
                            <Setter Target="mySplitView.IsPaneOpen" Value="True"/>
                        </VisualState.Setters>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>
    
            <SplitView x:Name="mySplitView" DisplayMode="CompactInline"
                       IsPaneOpen="False" CompactPaneLength="20">
                <!-- SplitView content -->
    
                <SplitView.Pane>
                    <!-- Pane content -->
                </SplitView.Pane>
            </SplitView>
        </Grid>
    </Page>
    

    Importante

    No exemplo anterior, a propriedade anexada VisualStateManager.VisualStateGroups está definida no elemento Grid. Ao usar StateTriggers, sempre verifique se VisualStateGroups está anexado ao primeiro filho da raiz para que os gatilhos entrem em vigor automaticamente. (Aqui, Grid é o primeiro filho do elemento raiz Page .)

    Sintaxe de propriedade anexada

    Em um VisualState, você normalmente define um valor para uma propriedade de controle ou para uma das propriedades anexadas do painel que contém o controle. Ao definir uma propriedade anexada, use parênteses ao redor do nome da propriedade anexada.

    Este exemplo mostra como definir a propriedade anexada RelativePanel.AlignHorizontalCenterWithPanel em um TextBox chamado myTextBox. O primeiro XAML usa a sintaxe ObjectAnimationUsingKeyFrames e o segundo usa a sintaxe Setter .

    <!-- Set an attached property using ObjectAnimationUsingKeyFrames. -->
    <ObjectAnimationUsingKeyFrames
        Storyboard.TargetProperty="(RelativePanel.AlignHorizontalCenterWithPanel)"
        Storyboard.TargetName="myTextBox">
        <DiscreteObjectKeyFrame KeyTime="0" Value="True"/>
    </ObjectAnimationUsingKeyFrames>
    
    <!-- Set an attached property using Setter. -->
    <Setter Target="myTextBox.(RelativePanel.AlignHorizontalCenterWithPanel)" Value="True"/>
    

    Gatilhos de estado personalizados

    Você pode estender a classe StateTrigger para criar gatilhos personalizados para uma ampla variedade de cenários. Por exemplo, você pode criar um StateTrigger para disparar estados diferentes com base no tipo de entrada e, em seguida, aumentar as margens em torno de um controle quando o tipo de entrada for touch. Ou crie um StateTrigger para aplicar estados diferentes com base na família de dispositivos em que o aplicativo é executado. Para obter exemplos de como criar gatilhos personalizados e usá-los para criar experiências de interface do usuário otimizadas de dentro de uma única exibição XAML, consulte o exemplo de gatilhos de estado.

    Estados e estilos visuais

    Você pode usar recursos de estilo em estados visuais para aplicar um conjunto de alterações de propriedade a vários controles. Para obter mais informações sobre como usar estilos, consulte Controles de estilo.

    Neste XAML simplificado do exemplo de gatilhos de estado, um recurso Style é aplicado a um Button para ajustar o tamanho e as margens da entrada do mouse ou do toque. Para obter o código completo e a definição do gatilho de estado personalizado, consulte o exemplo de gatilhos de estado.

    <Page ... >
        <Page.Resources>
            <!-- Styles to be used for mouse vs. touch/pen hit targets -->
            <Style x:Key="MouseStyle" TargetType="Rectangle">
                <Setter Property="Margin" Value="5" />
                <Setter Property="Height" Value="20" />
                <Setter Property="Width" Value="20" />
            </Style>
            <Style x:Key="TouchPenStyle" TargetType="Rectangle">
                <Setter Property="Margin" Value="15" />
                <Setter Property="Height" Value="40" />
                <Setter Property="Width" Value="40" />
            </Style>
        </Page.Resources>
    
        <RelativePanel>
            <!-- ... -->
            <Button Content="Color Palette Button" x:Name="MenuButton">
                <Button.Flyout>
                    <Flyout Placement="Bottom">
                        <RelativePanel>
                            <Rectangle Name="BlueRect" Fill="Blue"/>
                            <Rectangle Name="GreenRect" Fill="Green" RelativePanel.RightOf="BlueRect" />
                            <!-- ... -->
                        </RelativePanel>
                    </Flyout>
                </Button.Flyout>
            </Button>
            <!-- ... -->
        </RelativePanel>
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="InputTypeStates">
                <!-- Second set of VisualStates for building responsive UI optimized for input type.
                     Take a look at InputTypeTrigger.cs class in CustomTriggers folder to see how this is implemented. -->
                <VisualState>
                    <VisualState.StateTriggers>
                        <!-- This trigger indicates that this VisualState is to be applied when MenuButton is invoked using a mouse. -->
                        <triggers:InputTypeTrigger TargetElement="{x:Bind MenuButton}" PointerType="Mouse" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Target="BlueRect.Style" Value="{StaticResource MouseStyle}" />
                        <Setter Target="GreenRect.Style" Value="{StaticResource MouseStyle}" />
                        <!-- ... -->
                    </VisualState.Setters>
                </VisualState>
                <VisualState>
                    <VisualState.StateTriggers>
                        <!-- Multiple trigger statements can be declared in the following way to imply OR usage.
                             For example, the following statements indicate that this VisualState is to be applied when MenuButton is invoked using Touch OR Pen.-->
                        <triggers:InputTypeTrigger TargetElement="{x:Bind MenuButton}" PointerType="Touch" />
                        <triggers:InputTypeTrigger TargetElement="{x:Bind MenuButton}" PointerType="Pen" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Target="BlueRect.Style" Value="{StaticResource TouchPenStyle}" />
                        <Setter Target="GreenRect.Style" Value="{StaticResource TouchPenStyle}" />
                        <!-- ... -->
                    </VisualState.Setters>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
    </Page>