Partilhar via


Esquema

Este tópico descreve o sistema de layout do Windows Presentation Foundation (WPF). Entender como e quando os cálculos de layout ocorrem é essencial para criar interfaces de usuário no WPF.

Este tópico contém as seguintes seções:

Caixas delimitadoras de elementos

Ao pensar em layout no WPF, é importante entender a caixa delimitadora que envolve todos os elementos. Cada FrameworkElement consumido pelo sistema de layout pode ser entendido como um retângulo encaixado no layout. A LayoutInformation classe retorna os limites da alocação de layout ou slot de um elemento. O tamanho do retângulo é determinado calculando o espaço de tela disponível, o tamanho de quaisquer restrições, propriedades específicas de layout (como margem e preenchimento) e o comportamento individual do elemento pai Panel . Processando esses dados, o sistema de layout é capaz de calcular a posição de todos os filhos de um determinado Panel. É importante lembrar que as características de dimensionamento definidas no elemento pai, como um Border, afetam seus filhos.

A ilustração a seguir mostra um layout simples.

Captura de tela que mostra uma grade típica, sem caixa delimitadora sobreposta.

Esse layout pode ser obtido usando o seguinte XAML.

<Grid Name="myGrid" Background="LightSteelBlue" Height="150">
  <Grid.ColumnDefinitions>
    <ColumnDefinition Width="250"/>
  </Grid.ColumnDefinitions>
  <Grid.RowDefinitions>
    <RowDefinition />
    <RowDefinition />
    <RowDefinition />
  </Grid.RowDefinitions>
  <TextBlock Name="txt1" Margin="5" FontSize="16" FontFamily="Verdana" Grid.Column="0" Grid.Row="0">Hello World!</TextBlock>
  <Button Click="getLayoutSlot1" Width="125" Height="25" Grid.Column="0" Grid.Row="1">Show Bounding Box</Button>
  <TextBlock Name="txt2" Grid.Column="1" Grid.Row="2"/>
</Grid>

Um único TextBlock elemento é alojado num Grid. Enquanto o texto preenche apenas o canto superior esquerdo da primeira coluna, o espaço alocado para a TextBlock é realmente muito maior. A caixa delimitadora de qualquer FrameworkElement pode ser recuperada usando o GetLayoutSlot método. A ilustração a seguir mostra a caixa delimitadora do elemento TextBlock.

Captura de ecrã que mostra que a caixa delimitadora do TextBlock agora está visível.

Como mostrado pelo retângulo amarelo, o espaço alocado para o TextBlock elemento é realmente muito maior do que parece. À medida que elementos adicionais são adicionados ao Grid, essa alocação pode diminuir ou expandir, dependendo do tipo e tamanho dos elementos adicionados.

O slot de layout do TextBlock é traduzido para um Path usando o método GetLayoutSlot. Esta técnica pode ser útil para mostrar a caixa delimitadora de um elemento.

private void getLayoutSlot1(object sender, System.Windows.RoutedEventArgs e)
{
    RectangleGeometry myRectangleGeometry = new RectangleGeometry();
    myRectangleGeometry.Rect = LayoutInformation.GetLayoutSlot(txt1);
    Path myPath = new Path();
    myPath.Data = myRectangleGeometry;
    myPath.Stroke = Brushes.LightGoldenrodYellow;
    myPath.StrokeThickness = 5;
    Grid.SetColumn(myPath, 0);
    Grid.SetRow(myPath, 0);
    myGrid.Children.Add(myPath);
    txt2.Text = "LayoutSlot is equal to " + LayoutInformation.GetLayoutSlot(txt1).ToString();
}
Private Sub getLayoutSlot1(ByVal sender As Object, ByVal e As RoutedEventArgs)
    Dim myRectangleGeometry As New RectangleGeometry
    myRectangleGeometry.Rect = LayoutInformation.GetLayoutSlot(txt1)
    Dim myPath As New Path
    myPath.Data = myRectangleGeometry
    myPath.Stroke = Brushes.LightGoldenrodYellow
    myPath.StrokeThickness = 5
    Grid.SetColumn(myPath, 0)
    Grid.SetRow(myPath, 0)
    myGrid.Children.Add(myPath)
    txt2.Text = "LayoutSlot is equal to " + LayoutInformation.GetLayoutSlot(txt1).ToString()
End Sub

O Sistema de Layout

Em sua forma mais simples, layout é um sistema recursivo que leva a que um elemento seja dimensionado, posicionado e desenhado. Mais especificamente, o layout descreve o processo de medir e organizar os membros de uma coleção Panel de um elemento Children. O layout é um processo exigente. Quanto maior a Children arrecadação, maior o número de cálculos que devem ser feitos. A complexidade também pode ser introduzida com base no comportamento de layout definido pelo Panel elemento proprietário da coleção. Um relativamente simples Panel, como Canvaso , pode ter um desempenho significativamente melhor do que um mais complexo Panel, como Grido .

Cada vez que uma criança UIElement muda de posição, ela pode provocar uma nova passagem pelo sistema de layout. Portanto, é importante entender os eventos que podem invocar o sistema de layout, pois a invocação desnecessária pode levar a um desempenho ruim do aplicativo. A seguir descrevemos o processo que ocorre quando o sistema de layout é invocado.

  1. Um elemento filho UIElement inicia o processo de layout medindo primeiro suas propriedades principais.

  2. As propriedades de dimensionamento definidas em FrameworkElement são avaliadas, como Width, Heighte Margin.

  3. Lógica específica de Panel é aplicada, tal como a direção de Dock ou o empilhamento de Orientation.

  4. O conteúdo é organizado depois de todas as crianças terem sido medidas.

  5. A Children coleção é apresentada no ecrã.

  6. O processo é invocado novamente se adicionais Children forem adicionados à coleção, um LayoutTransform for aplicado ou o método UpdateLayout for chamado.

Esse processo e como ele é invocado são definidos com mais detalhes nas seções a seguir.

Medir e organizar as crianças

O sistema de layout completa dois passes para cada membro da coleção Children, um passe de medida e um passe de arranjo. Cada elemento Panel fornece os seus próprios métodos MeasureOverride e ArrangeOverride para alcançar o seu próprio comportamento de layout específico.

Durante a passagem da medida, cada membro da Children coleção é avaliado. O processo começa com uma chamada para o Measure método. Esse método é chamado dentro da implementação do elemento pai Panel e não precisa ser chamado explicitamente para que o layout ocorra.

Primeiro, as propriedades de tamanho nativo do UIElement são avaliadas, como Clip e Visibility. Isso gera um valor chamado constraintSize que é passado para MeasureCore.

Em segundo lugar, as propriedades da estrutura definidas em FrameworkElement são processadas, o que afeta o valor de constraintSize. Essas propriedades geralmente descrevem as características de dimensionamento do subjacente UIElement, como seu Height, Width, Margin, e Style. Cada uma dessas propriedades pode alterar o espaço necessário para exibir o elemento. MeasureOverride é então chamado com constraintSize como parâmetro.

Observação

Há uma diferença entre as propriedades de Height e Width e ActualHeight e ActualWidth. Por exemplo, a ActualHeight propriedade é um valor calculado com base em outras entradas de altura e no sistema de layout. O valor é definido pelo próprio sistema de layout, com base em um passo de renderização real, e pode, portanto, ficar ligeiramente atrás do valor definido de propriedades, como Height, que são a base da alteração de entrada.

Como ActualHeight é um valor calculado, você deve estar ciente de que pode haver várias ou incrementais alterações relatadas nele como resultado de várias operações pelo sistema de layout. O sistema de layout pode estar a calcular o espaço de medida necessário para elementos filho, restrições impostas pelo elemento pai e assim por diante.

O objetivo final do processo de medição é que a criança determine o seu DesiredSize, que ocorre durante a invocação de MeasureCore. O DesiredSize valor é armazenado por Measure para uso durante o processo de organização do conteúdo.

O passe de organização começa com uma chamada para o método Arrange. Durante o passo de arranjo, o elemento pai Panel gera um retângulo que representa os limites da criança. Esse valor é passado para o ArrangeCore método de processamento.

O método ArrangeCore avalia o DesiredSize da criança e avalia quaisquer margens adicionais que possam afetar o tamanho renderizado do elemento. ArrangeCore gera um arrangeSize, que é passado para o ArrangeOverride método do Panel como um parâmetro. ArrangeOverride gera o finalSize da criança. Finalmente, o ArrangeCore método faz uma avaliação final das propriedades de deslocamento, como margem e alinhamento, e coloca o filho dentro de seu slot de layout. A criança não tem de preencher (e frequentemente não preenche) todo o espaço atribuído. O controle é então retornado ao pai Panel e o processo de layout é concluído.

Elementos do painel e comportamentos de layout personalizados

WPF inclui um grupo de elementos que derivam de Panel. Esses Panel elementos permitem muitos layouts complexos. Por exemplo, o empilhamento de elementos pode ser facilmente alcançado usando o elemento StackPanel, enquanto layouts mais complexos e fluidos são possíveis usando um Canvas.

A tabela a seguir resume os elementos de layout Panel disponíveis.

Nome do painel Descrição
Canvas Define uma área na qual você pode posicionar explicitamente elementos filho por coordenadas relativas à área Canvas.
DockPanel Define uma área na qual você pode organizar elementos filho horizontal ou verticalmente, em relação uns aos outros.
Grid Define uma área de grade flexível que consiste em colunas e linhas.
StackPanel Organiza os elementos filho em uma só linha que pode estar orientada horizontalmente ou verticalmente.
VirtualizingPanel Fornece uma estrutura para Panel elementos que virtualizam a sua coleção de dados de filhos. Esta é uma classe abstrata.
WrapPanel Posiciona os elementos filho na posição sequencial da esquerda para a direita, quebrando o conteúdo para a próxima linha na borda da caixa de contenção. A ordenação subsequente ocorre sequencialmente de cima para baixo ou da direita para a esquerda, dependendo do valor da Orientation propriedade.

Para aplicações que exigem um layout que não é possível através de nenhum dos elementos predefinidos Panel, os comportamentos de layout personalizados podem ser alcançados ao herdar de Panel e substituir os métodos MeasureOverride e ArrangeOverride.

Considerações sobre desempenho de layout

O layout é um processo recursivo. Cada elemento filho em uma Children coleção é processado durante cada invocação do sistema de layout. Como resultado, o acionamento do sistema de layout deve ser evitado quando não for necessário. As considerações a seguir podem ajudá-lo a obter um melhor desempenho.

  • Esteja ciente de quais alterações de valor de propriedade forçarão uma atualização recursiva pelo sistema de layout.

    As propriedades de dependência cujos valores podem fazer com que o sistema de layout seja inicializado são marcadas com sinalizadores públicos. AffectsMeasure e AffectsArrange informam sobre quais alterações no valor da propriedade forçarão uma atualização recursiva pelo sistema de layout. Em geral, qualquer propriedade que possa afetar o tamanho da caixa delimitadora de um elemento deve ter o sinalizador AffectsMeasure definido como verdadeiro. Para obter mais informações, consulte Visão Geral das Propriedades de Dependência.

  • Sempre que possível, use um RenderTransform em vez de um LayoutTransform.

    A LayoutTransform pode ser uma maneira muito útil de afetar o conteúdo de uma interface do usuário (UI). No entanto, se o efeito da transformação não tiver que afetar a posição de outros elementos, é melhor usar um RenderTransform em vez disso, porque RenderTransform não invoca o sistema de layout. LayoutTransform aplica sua transformação e força uma atualização de layout recursiva para levar em conta a nova posição do elemento afetado.

  • Evite chamadas desnecessárias para UpdateLayout.

    O UpdateLayout método força uma atualização de layout recursiva e frequentemente não é necessário. A menos que tenhas certeza de que uma atualização completa é necessária, confia no sistema de layout para chamar este método por ti.

  • Ao trabalhar com uma coleção grande Children , considere usar um VirtualizingStackPanel em vez de um regular StackPanel.

    Ao virtualizar a coleção de filhos, o VirtualizingStackPanel mantém na memória apenas os objetos que estão atualmente no ViewPort do pai. Como resultado, o desempenho é substancialmente melhorado na maioria dos cenários.

Renderização de subpixels e arredondamento de layout

O sistema gráfico WPF usa unidades independentes do dispositivo para permitir a resolução e independência do dispositivo. Cada pixel independente do dispositivo é dimensionado automaticamente com a configuração de pontos por polegada (dpi) do sistema. Isso fornece aos aplicativos WPF o dimensionamento adequado para diferentes configurações de dpi e torna o aplicativo automaticamente compatível com dpi.

No entanto, essa independência de dpi pode criar bordas irregulares ao serem renderizadas devido ao anti-alias. Esses artefatos, normalmente vistos como bordas borradas ou semitransparentes, podem ocorrer quando a localização de uma borda cai no meio de um pixel do dispositivo em vez de entre os pixels do dispositivo. O sistema de layout fornece uma maneira de lidar com isso através do arredondamento. O arredondamento de layout ocorre quando o sistema de layout arredonda quaisquer valores de pixel não inteiros durante o processo de layout.

O arredondamento de layout está desativado por padrão. Para habilitar o arredondamento de layout, defina a propriedade UseLayoutRounding como true em qualquer FrameworkElement elemento. Por ser uma propriedade de dependência, o valor será propagado para todas as crianças na árvore visual. Para habilitar o arredondamento de layout para toda a interface do usuário, defina UseLayoutRounding como true no contêiner raiz. Para obter um exemplo, consulte UseLayoutRounding.

O que vem a seguir

Entender como os elementos são medidos e organizados é o primeiro passo para entender o layout. Para obter mais informações sobre os elementos disponíveis Panel , consulte Visão geral dos painéis. Para entender melhor as várias propriedades de posicionamento que podem afetar o layout, consulte Visão geral de alinhamento, margens e preenchimento. Quando estiver pronto para juntar tudo em um aplicativo leve, consulte Passo a passo: Meu primeiro aplicativo de desktop WPF.

Ver também