Disposição
Este tópico descreve o sistema de layout do WPF (Windows Presentation Foundation). 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:
Elementos do Painel e Comportamentos de Layout Personalizados
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 considerado como um retângulo que é inserido no layout. A classe LayoutInformation retorna os limites da alocação de layout de um elemento ou espaço reservado. 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 Panel pai. Ao processar 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.
Esse layout pode ser obtido usando o XAML a seguir.
<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 elemento TextBlock é hospedado em um Grid. Embora o texto preencha apenas o canto superior esquerdo da primeira coluna, o espaço alocado para o TextBlock é, na verdade, muito maior. A caixa delimitadora de qualquer FrameworkElement pode ser recuperada usando o método GetLayoutSlot. A ilustração a seguir mostra a caixa delimitadora do elemento TextBlock.
Conforme mostrado pelo retângulo amarelo, o espaço alocado para o elemento TextBlock é, na verdade, 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 do tamanho dos elementos adicionados.
O slot de layout do TextBlock é convertido em um Path usando o método GetLayoutSlot. Essa técnica pode ser útil para exibir 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
No seu nível mais simples, o layout é um sistema recursivo que faz com que um elemento seja dimensionado, posicionado e desenhado. Mais especificamente, o layout descreve o processo de medir e organizar os membros da coleção Children de um elemento Panel. O processo de layout é intensivo. Quanto maior a coleção Children, 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 elemento Panel que possui a coleção. Um Panelrelativamente simples, como Canvas, pode ter um desempenho significativamente melhor do que um Panelmais complexo, como Grid.
Cada vez que um filho UIElement altera sua posição, ele tem o potencial de disparar 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, descreve o processo que ocorre quando o sistema de layout é invocado.
O Filho UIElement inicia o processo de layout começando a medir suas propriedades principais.
As propriedades de dimensionamento definidas em FrameworkElement, como Width, Heighte Margin, são avaliadas.
Panellógica específica é aplicada, como a direção Dock ou o empilhamento Orientation.
O conteúdo é organizado após todas as crianças terem sido medidas.
A coleção Children é exibida na tela.
O processo será invocado novamente se forem adicionados Children adicionais à 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.
Medindo e organizando crianças
O sistema de layout conclui duas passagens para cada membro da coleção Children, uma passagem de medida e uma passagem de organização. Cada filho Panel fornece seus próprios métodos MeasureOverride e ArrangeOverride para obter seu comportamento específico de layout.
Durante a passagem da medida, cada membro da coleção Children é avaliado. O processo começa com uma chamada para o método Measure. Esse método é chamado dentro da implementação do elemento Panel pai 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 dos UIElementsubjacentes, como seus Height, Width, Margine Style. Cada uma dessas propriedades pode alterar o espaço necessário para exibir o elemento. MeasureOverride é chamado com constraintSize
como parâmetro.
Nota
Há uma diferença entre as propriedades de Height e Width e ActualHeight e ActualWidth. Por exemplo, a propriedade ActualHeight é um valor calculado com base em outras entradas de altura e no sistema de layout. O valor é atribuído pelo próprio sistema de layout, com base em uma passagem de renderização real e, portanto, pode ficar um pouco atrás do valor configurado das propriedades, como Height, que constituem 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 do sistema de layout. O sistema de layout pode estar calculando o espaço de medida necessário para elementos filho, restrições pelo elemento pai e assim por diante.
O objetivo final da aprovação da medida é que a criança determine seu DesiredSize, que ocorre durante a chamada MeasureCore. O valor DesiredSize é armazenado por Measure para ser usado durante a organização do conteúdo.
O processo de ordenação começa com uma chamada para o método Arrange. Durante a fase de arranjo, o elemento pai Panel gera um retângulo que representa os limites do elemento filho. Esse valor é passado para o método ArrangeCore para processamento.
O método ArrangeCore avalia a propriedade DesiredSize da criança e analisa quaisquer margens adicionais que possam afetar o tamanho renderizado do elemento. ArrangeCore gera um arrangeSize
, que é passado para o método ArrangeOverride do Panel como um parâmetro. ArrangeOverride gera o finalSize
da criança. Por fim, o método ArrangeCore faz uma avaliação final das propriedades de offset, como margem e alinhamento, e posiciona o elemento filho em seu slot de layout. O filho não precisa (e frequentemente não) preencher todo o espaço alocado. Em seguida, o controle é retornado para o Panel pai e o processo de layout é concluído.
Elementos de Painel e Comportamentos Personalizados de Layout
O WPF inclui um grupo de elementos que derivam de Panel. Esses elementos Panel habilitam muitos layouts complexos. Por exemplo, elementos empilhados podem ser facilmente obtidos usando o elemento StackPanel, enquanto layouts de fluxo livre e mais complexos são possíveis usando um elemento Canvas.
A tabela a seguir resume os elementos de layout disponíveis Panel.
Nome do painel | Descrição |
---|---|
Canvas | Define uma área na qual você pode posicionar explicitamente elementos filho por coordenadas relativas à área de 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 elementos filho em uma única linha que pode ser orientada horizontal ou verticalmente. |
VirtualizingPanel | Fornece uma estrutura para elementos Panel que virtualizam a coleção de dados dos filhos. Esta é uma classe abstrata. |
WrapPanel | Posiciona elementos filho em 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 propriedade Orientation. |
Para aplicativos que exigem um layout que não é possível usando qualquer um dos elementos de Panel predefinidos, os comportamentos de layout personalizado podem ser obtidos herdando de Panel e substituindo os métodos MeasureOverride e ArrangeOverride.
Considerações sobre o desempenho do layout
Layout é um processo recursivo. Cada elemento filho em uma coleção Children é processado durante cada vez que o sistema de layout é invocado. 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.
Lembre-se 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 fornecem pistas úteis sobre quais alterações de valor de 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 um flag AffectsMeasure definido como true. Para obter mais informações, consulte Visão geral das propriedades de dependência.
Quando possível, use um RenderTransform em vez de um LayoutTransform.
Uma LayoutTransform pode ser uma maneira muito útil de afetar o conteúdo de uma UI (interface do usuário). No entanto, se o efeito da transformação não precisar 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 recursivo a considerar a nova posição do elemento afetado.
Evite chamadas desnecessárias para UpdateLayout.
O método UpdateLayout força uma atualização de layout recursivo e frequentemente não é necessário. A menos que você tenha certeza de que uma atualização completa é necessária, permita que o sistema de layout chame esse método para você.
Ao trabalhar com uma grande coleção de Children, considere usar um VirtualizingStackPanel em vez de um StackPanelregular.
Ao virtualizar a coleção filho, o VirtualizingStackPanel mantém apenas objetos na memória que estão atualmente dentro do ViewPort do pai. Como resultado, o desempenho é substancialmente melhorado na maioria dos cenários.
Renderização de sub pixel e arredondamento de layout
O sistema gráfico WPF usa unidades independentes de dispositivo para habilitar a resolução e a independência do dispositivo. Cada pixel independente de dispositivo é automaticamente escalado conforme a configuração de pontos por polegada (dpi) do sistema. Isso fornece dimensionamento adequado de aplicativos WPF para diferentes configurações de dpi e torna o aplicativo automaticamente com reconhecimento de dpi.
No entanto, essa independência de resolução (dpi) pode criar renderização irregular das bordas devido ao anti-aliasing. Esses artefatos, normalmente vistos como bordas desfocadas ou semi-transparentes, podem ocorrer quando a posição de uma borda cai no centro de um pixel de dispositivo, em vez de entre dois pixels de dispositivo. O sistema de layout fornece uma maneira de ajustar isso por meio do arredondamento de layout. O arredondamento de layout ocorre quando o sistema de layout arredonda todos os valores de pixel não integrais durante o processo de layout.
O arredondamento de layout é desabilitado por padrão. Para habilitar o arredondamento de layout, defina a propriedade UseLayoutRounding para true
em qualquer FrameworkElement. Como é uma propriedade vinculada, o valor será transmitido para todos os filhos na árvore visual. Para habilitar o arredondamento de layout para toda a interface do usuário, defina UseLayoutRounding para 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 é a primeira etapa na compreensão do layout. Para obter mais informações sobre os elementos de Panel disponíveis, 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 montar tudo em um aplicativo leve, consulte Passo a passo: Meu primeiro aplicativo desktop do WPF.
Consulte também
- FrameworkElement
- UIElement
- Visão geral dos painéis de
- Visão geral de alinhamento, margens e preenchimento
- Layout e Design
.NET Desktop feedback