Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Um painel é um objeto que fornece um comportamento de layout para elementos filhos que contém, quando o sistema de layout XAML (Extensible Application Markup Language) é executado e a interface de utilizador da aplicação é renderizada.
APIs importantes: Panel, ArrangeOverrideMeasureOverride
Você pode definir painéis personalizados para layout XAML derivando uma classe personalizada da classe Panel . Você fornece comportamento para seu painel substituindo o MeasureOverride e ArrangeOverride , fornecendo lógica que mede e organiza os elementos filho.
O painel classe base
Para definir uma classe de painel personalizada, pode derivar diretamente da classe Panel ou de uma das classes práticas de painel que não estão seladas, como Grid ou StackPanel. É mais fácil derivar do Painel, porque pode ser difícil contornar a lógica de layout de um painel que já possui comportamento de layout. Além disso, um painel com comportamento pode ter propriedades existentes que não são relevantes para os recursos de layout do painel.
O seu painel personalizado herda estas APIs a partir de Painel:
- A propriedade das crianças.
- O Background, ChildrenTransitions e propriedades de IsItemsHost e os identificadores de propriedade de dependência. Nenhuma dessas propriedades é virtual, portanto, normalmente não as substitui. Normalmente, você não precisa dessas propriedades para cenários de painel personalizados, nem mesmo para ler valores.
- Os métodos de substituição de layout MeasureOverride e ArrangeOverride. Estes foram originalmente definidos por FrameworkElement. A classe Base Panel não substitui isso, mas painéis práticos como Grid têm implementações de substituição que são implementadas como código nativo e são executadas pelo sistema. Fornecer novas implementações (ou aditivas) para ArrangeOverride e MeasureOverride é a maior parte do trabalho necessário para definir um painel personalizado.
- Todas as restantes APIs do FrameworkElement, UIElement e DependencyObject, como Height, Visibility e assim por diante. Por vezes, tu fazes referência a valores dessas propriedades nas tuas sobreposições de layout, mas elas não são virtuais, portanto, normalmente não as sobrepões ou substituis.
Esse foco aqui é descrever conceitos de layout XAML, para que você possa considerar todas as possibilidades de como um painel personalizado pode e deve se comportar no layout. Se você preferir ir direto e ver um exemplo de implementação de painel personalizado, consulte BoxPanel, um exemplo de painel personalizado.
A propriedade Crianças
A propriedade Children é relevante para um painel personalizado porque todas as classes derivadas de Panel usam a propriedade Children como o local para armazenar seus elementos filho contidos em uma coleção. Children está designada como a propriedade de conteúdo XAML para a classe Panel, e todas as classes derivadas de Panel podem herdar esse comportamento de propriedade de conteúdo XAML. Se uma propriedade for designada como propriedade de conteúdo XAML, isso significa que a marcação XAML pode omitir um elemento de propriedade ao especificar essa propriedade na marcação, e os valores são definidos como filhos de marcação imediata (o "conteúdo"). Por exemplo, se você derivar uma classe chamada CustomPanel de Panel que não define nenhum novo comportamento, ainda poderá usar esta marcação:
<local:CustomPanel>
<Button Name="button1"/>
<Button Name="button2"/>
</local:CustomPanel>
Quando um analisador XAML lê essa marcação,
O tipo de coleção que mantém o valor da propriedade Children é a classe UIElementCollection. UIElementCollection é uma coleção fortemente tipada que usa UIElement como seu tipo de item fixado. UIElement é um tipo base herdado por centenas de tipos práticos de elementos da interface de usuário, por isso, a imposição de tipo aqui é intencionalmente flexível. Mas isso impõe que você não poderia ter um Pincel como filho direto de um Painel , e geralmente significa que apenas os elementos que se espera que sejam visíveis na interface do utilizador e participem do layout serão encontrados como elementos filho em um Painel .
Normalmente, um painel personalizado aceita qualquer elemento filho UIElement através de uma definição XAML, utilizando simplesmente as características da propriedade Children as-is. Como um cenário avançado, é possível oferecer suporte à verificação adicional de tipos de elementos filho ao iterar sobre a coleção nas suas substituições de layout.
Além de percorrer a coleção
Sobrepondo os métodos de layout
O modelo básico para os métodos de substituição de layout (MeasureOverride e ArrangeOverride) é que eles devem percorrer todos os filhos e invocar o método de layout específico de cada elemento filho. O primeiro ciclo de layout começa quando o sistema de layout XAML define o visual para a janela raiz. Como cada pai invoca layout em seus filhos, isso propaga uma chamada para métodos de layout para cada elemento possível da interface do usuário que supostamente faz parte de um layout. No layout XAML, há dois estágios: medir e, em seguida, organizar.
Você não obtém nenhum comportamento de método de layout incorporado para os métodos MeasureOverride e ArrangeOverride da classe base Panel. Os itens em (filhos) não serão automaticamente renderizados como parte da árvore visual XAML. Cabe a si tornar os itens conhecidos para o processo de layout, invocando métodos de layout em cada um dos itens que encontrar em Children através de um passe de layout dentro das suas implementações de MeasureOverride e ArrangeOverride.
Não há razão para chamar implementações de base em substituições de layout, a menos que você tenha sua própria herança. Os métodos nativos para o comportamento de layout (se existirem) são executados independentemente disso, e não chamar a implementação de base de substituições não impedirá que o comportamento nativo aconteça.
Durante a passagem de medida, sua lógica de layout consulta cada elemento filho para o tamanho desejado, chamando o método Measure nesse elemento filho. Chamar o método Measure estabelece o valor para a propriedade DesiredSize. O valor de retorno de MeasureOverride é o tamanho desejado para o próprio painel.
Durante a fase de organização, as posições e tamanhos dos elementos secundários são determinados no espaço x-y e a composição do layout é preparada para renderização. Seu código deve chamar Organizar em cada elemento filho em Crianças para que o sistema de layout detete que o elemento pertence ao layout. O Arrange é uma chamada precursora da composição e renderização; informa o sistema de layout sobre a localização desse elemento, quando a composição é enviada para renderização.
Muitas propriedades e valores contribuem para como a lógica de layout funcionará em tempo de execução. Uma maneira de pensar sobre o processo de layout é que os elementos sem filhos (geralmente o elemento aninhado mais profundamente na interface do usuário) são os que podem finalizar as medições primeiro. Eles não têm quaisquer dependências de elementos filhos que influenciem o seu tamanho desejado. Eles podem ter seus próprios tamanhos desejados, e estas são sugestões de tamanho até que o layout realmente ocorra. Em seguida, o passe de medida continua subindo a árvore visual até que o elemento raiz tenha suas medidas e todas as medidas possam ser finalizadas.
O layout candidato deve caber na janela atual do aplicativo ou partes da interface do usuário serão cortadas. Os painéis geralmente são o local onde a lógica de recorte é determinada. A lógica do painel pode determinar o tamanho disponível na implementação do MeasureOverride
Uma grande parte do que faz o sistema de layout funcionar é que qualquer elemento baseado em FrameworkElement já tem algum de seu próprio comportamento inerente ao agir como filho em um contêiner. Por exemplo, há várias APIs do FrameworkElement que descrevem o comportamento do layout ou são necessárias para que o layout funcione corretamente. Estes são, entre outros:
- DesiredSize (na verdade, uma propriedade UIElement)
- AlturaReal e LarguraReal
- Altura e Largura
- Margem
- LayoutEvento atualizado
- HorizontalAlignment e VerticalAlignment
- e métodos de ArrangeOverride e MeasureOverride
- Organizar e Medir métodos: eles têm implementações nativas definidas no nível FrameworkElement, que manipulam a ação de layout no nível do elemento
MeasureOverride
O método MeasureOverride tem um valor de retorno que é usado pelo sistema de layout como o DesiredSize inicial para o próprio painel, quando o método Measure é chamado no painel por seu pai no layout. As escolhas lógicas dentro do método são tão importantes quanto o que ele retorna, e a lógica muitas vezes influencia o valor que é retornado.
Todas as implementações de MeasureOverride de
Aqui está um esqueleto muito básico de um método MeasureOverride:
protected override Size MeasureOverride(Size availableSize)
{
Size returnSize; //TODO might return availableSize, might do something else
//loop through each Child, call Measure on each
foreach (UIElement child in Children)
{
child.Measure(new Size()); // TODO determine how much space the panel allots for this child, that's what you pass to Measure
Size childDesiredSize = child.DesiredSize; //TODO determine how the returned Size is influenced by each child's DesiredSize
//TODO, logic if passed-in Size and net DesiredSize are different, does that matter?
}
return returnSize;
}
Os elementos geralmente têm um tamanho natural no momento em que estão prontos para o layout. Após a aprovação da medida, o DesiredSize de
Alguns elementos não têm um tamanho natural porque têm valores de automático para de altura e de largura. Esses elementos usam o tamanho completo disponível availableSize, porque é isso que um valor Auto representa: ajustar o elemento ao tamanho máximo disponível, que o pai de layout imediato comunica ao chamar Measure com availableSize. Na prática, há sempre alguma medida para a qual uma interface do usuário é dimensionada (mesmo que essa seja a janela de nível superior). Eventualmente, o passo de medida resolve todos os valores
É permitido passar um tamanho para Medida que tenha pelo menos uma dimensão infinita, para indicar que o painel pode ajustar-se às medidas do seu conteúdo. Cada elemento filho que está sendo medido define seu valor DesiredSize usando seu tamanho natural. Em seguida, durante a passagem de organização, o painel normalmente organiza usando esse tamanho.
Elementos de texto como TextBlock têm as dimensões ActualWidth e ActualHeight calculadas com base na sua cadeia de texto e propriedades de texto, mesmo que nenhum valor de altura ou largura esteja definido, e essas dimensões devem ser respeitadas pela lógica do teu painel. Recortar texto é uma experiência de interface do usuário particularmente ruim.
Mesmo que a sua implementação não use as medidas de tamanho desejadas, é melhor chamar o método Measure em cada elemento filho, porque há comportamentos internos e nativos que são acionados quando Measure é chamado. Para que um elemento participe do layout, cada subelemento deve ter Measure chamado durante a etapa de medição e o método Arrange chamado durante a etapa de organização. Chamar esses métodos define sinalizadores internos no objeto e preenche valores (como a propriedade DesiredSize ) que a lógica de layout do sistema precisa quando cria a árvore visual e renderiza a interface do usuário.
O valor de retorno MeasureOverride baseia-se na lógica do painel que interpreta o DesiredSize ou outras considerações de tamanho para cada um dos elementos filho em Children quando Measure é chamado neles. O que fazer com valores de DesiredSize de crianças e como o MeasureOverride valor de retorno deve usá-los depende da interpretação da sua própria lógica. Normalmente, você não soma os valores sem modificação, porque a entrada de MeasureOverride geralmente é um tamanho fixo disponível que está sendo sugerido pelo pai do painel. Se exceder esse tamanho, o próprio painel poderá ser cortado. Normalmente, você compararia o tamanho total das crianças com o tamanho disponível do painel e faria ajustes, se necessário.
Dicas e orientações
- Idealmente, um painel personalizado deve ser adequado para ser o primeiro verdadeiro visual numa composição de interfaces do utilizador, talvez a um nível imediatamente abaixo de Página, ControladorDeUtilizador ou de outro elemento que constitua a raiz da página XAML. Em implementações de MeasureOverride, não retorne rotineiramente a entrada Size sem examinar os valores. Se o retorno Tamanho tiver um valor de Infinito, isso poderá gerar exceções na lógica de layout de tempo de execução. Um valor Infinity pode vir da janela principal do aplicativo, que é rolável e, portanto, não tem uma altura máxima. Outros conteúdos roláveis podem ter o mesmo comportamento.
- Outro erro comum nas implementações de MeasureOverride é devolver um novo tamanho padrão (os valores de altura e largura são 0). Você pode começar com esse valor, e pode até ser o valor correto se o painel determinar que nenhum dos filhos deve ser renderizado. Mas, um tamanho padrão de resulta em que o seu painel não seja dimensionado corretamente no seu host. Ele não solicita espaço na interface do usuário e, portanto, não obtém espaço e não renderiza. Todo o código do painel pode estar a funcionar bem, mas o utilizador ainda não verá o painel ou o seu conteúdo se ele estiver a ser composto com altura zero e largura zero.
- Dentro das substituições, evite a tentação de converter elementos filho para FrameworkElement e use propriedades que são calculadas como resultado do layout, particularmente ActualWidth e ActualHeight. Para os cenários mais comuns, você pode basear a lógica no valor de DesiredSize
filho e não precisará de nenhuma das propriedades ouHeight relacionadas de um elemento filho. Para casos especializados, onde você sabe o tipo de elemento e tem informações adicionais, por exemplo, o tamanho natural de um arquivo de imagem, você pode usar as informações especializadas do elemento porque não é um valor que está sendo ativamente alterado por sistemas de layout. A inclusão de propriedades calculadas por layout como parte da lógica de layout aumenta substancialmente o risco de definir um loop de layout não intencional. Esses loops causam uma condição em que um layout válido não pode ser criado e o sistema pode lançar um LayoutCycleException se o loop não for recuperável.Width - Os painéis normalmente dividem seu espaço disponível entre vários elementos filho, embora a forma exata como o espaço é dividido varie. Por exemplo, a grelha implementa a lógica de layout que utiliza os seus valores de RowDefinition e de ColumnDefinition para dividir o espaço nas células da grelha , suportando tanto tamanhos em estrelas como valores em píxeis. Se forem valores de pixel, o tamanho disponível para cada filho já é conhecido, então é isso que é passado como tamanho de entrada para um estilo de grade Measure.
- Os próprios painéis podem introduzir espaço reservado para almofadagem entre itens. Se você fizer isso, certifique-se de expor as medidas como uma propriedade distinta de Margem ou de qualquer propriedade Preenchimento.
- Os elementos podem ter valores para suas ActualWidth e propriedades ActualHeight com base em um passo de layout anterior. Se os valores mudarem, o código da interface de utilizador da aplicação pode colocar manipuladores para LayoutUpdated nos elementos se houver uma lógica especial a executar, mas a lógica do painel normalmente não precisa de verificar alterações com o manuseamento de eventos. O sistema de layout já está fazendo as determinações de quando voltar a executar o layout, uma vez que uma propriedade relevante ao layout alterou o valor, e o MeasureOverride de ou o ArrangeOverride de de um painel são chamados automaticamente nas circunstâncias apropriadas.
ArrangeOverride
O método ArrangeOverride tem um valor de retorno Size que é usado pelo sistema de layout ao renderizar o próprio painel, quando o método Arrange é chamado no painel por seu pai no layout. É típico que a entrada finalSize e o tamanho retornado por ArrangeOverrideSize sejam os mesmos. Se não estiverem, isso significa que o painel está a tentar tornar-se um tamanho diferente daquele que os outros participantes do layout afirmam estar disponível. O tamanho final foi baseado na execução anterior da passagem de medição do layout através do código do painel, por isso, retornar um tamanho diferente não é típico: significa que está a ignorar deliberadamente a lógica de medição.
Não retorne um Tamanho com um componente Infinity. Tentar usar esse Tamanho gera uma exceção da disposição interna.
Todas as implementações ArrangeOverride devem percorrer Children e chamar o método Arrange em cada elemento filho. Tal como Medida, Organizar não tem um valor de retorno. Ao contrário do Measure, nenhuma propriedade calculada é definida como resultado disso (contudo, o elemento em questão normalmente dispara um evento LayoutUpdated).
Aqui está um esqueleto muito básico de um ArrangeOverride método:
protected override Size ArrangeOverride(Size finalSize)
{
//loop through each Child, call Arrange on each
foreach (UIElement child in Children)
{
Point anchorPoint = new Point(); //TODO more logic for topleft corner placement in your panel
// for this child, and based on finalSize or other internal state of your panel
child.Arrange(new Rect(anchorPoint, child.DesiredSize)); //OR, set a different Size
}
return finalSize; //OR, return a different Size, but that's rare
}
A passagem de layout pode acontecer sem ser precedida por uma passagem de medida. No entanto, isso só acontece quando o sistema de layout determinou que nenhuma propriedade foi alterada que teria afetado as medições anteriores. Por exemplo, se um alinhamento mudar, não há necessidade de remedir esse elemento específico porque o DesiredSize não mudaria quando a escolha de alinhamento fosse alterada. Por outro lado, se ActualHeight mudar em qualquer elemento de um layout, uma nova passagem de medida será necessária. O sistema de layout deteta automaticamente as alterações verdadeiras da medida e invoca o passo de medida novamente e, em seguida, executa outro passo de organização.
A entrada para Organizar aceita um valor Rect. A maneira mais comum de construir este Rect é usar o construtor que tem um parâmetro Point e um parâmetro Size. O Ponto é o ponto onde o canto superior esquerdo da caixa delimitadora do elemento deve ser colocado. O tamanho é a dimensão usada para renderizar esse elemento específico. Você frequentemente usa o DesiredSize para esse elemento como este valor de Size, porque estabelecer o DesiredSize para todos os elementos envolvidos no layout era o objetivo da etapa de medição do layout. (A etapa de medição determina o dimensionamento total dos elementos de forma iterativa para que o sistema de layout possa otimizar a forma como os elementos são colocados assim que chegam à etapa de arranjo.)
O que normalmente varia entre implementações ArrangeOverride é a lógica pela qual o painel determina o Point componente de como ele organiza cada filho. Um painel de posicionamento absoluto, como Canvas, usa as informações explícitas de posicionamento que obtém de cada elemento por meio dos valores de Canvas.Left e Canvas.Top. Um painel de divisão de espaço como Grid teria operações matemáticas que dividiriam o espaço disponível em células e cada célula teria um valor x-y para onde seu conteúdo deveria ser colocado e organizado. Um painel adaptável como o StackPanel pode estar se expandindo para ajustar o conteúdo em sua dimensão de orientação.
Há ainda influências adicionais de posicionamento em elementos no layout, além do que você controla diretamente e passa para Organizar. Eles vêm da implementação nativa interna do
Painéis e controlos
Evite colocar a funcionalidade em um painel personalizado que, em vez disso, deve ser criado como um controle personalizado. A função de um painel é apresentar qualquer conteúdo de elemento filho que exista dentro dele, como uma função de layout que acontece automaticamente. O painel pode adicionar decorações ao conteúdo (semelhante a como uma borda adiciona a borda ao redor do elemento que apresenta) ou executar outros ajustes relacionados ao layout, como preenchimento. Mas isso é o mais longe que você deve ir ao estender a saída da árvore visual além de relatar e usar informações das crianças.
Se houver alguma interação acessível ao usuário, você deve escrever um controle personalizado, não um painel. Por exemplo, um painel não deve adicionar áreas de rolagem ao conteúdo que apresenta, mesmo que o objetivo seja impedir o corte, porque as barras de rolagem, cursores, entre outros, são partes interativas de controle. (O conteúdo pode ter barras de rolagem, afinal, mas você deve deixar isso para a lógica da criança. Não force isso adicionando rolagem como uma operação de layout.) Você pode criar um controle e também escrever um painel personalizado que desempenha um papel importante na árvore visual desse controle, quando se trata de apresentar conteúdo nesse controle. Mas o controle e o painel devem ser objetos de código distintos.
Uma razão pela qual a distinção entre controle e painel é importante é por causa da automação da interface do usuário da Microsoft e acessibilidade. Os painéis fornecem um comportamento de layout visual, não um comportamento lógico. Como um elemento da interface do usuário aparece visualmente não é um aspeto da interface do usuário que normalmente é importante para cenários de acessibilidade. Acessibilidade é expor as partes de um aplicativo que são logicamente importantes para entender uma interface do usuário. Quando a interação é necessária, os controles devem expor as possibilidades de interação para a infraestrutura de automação da interface do usuário. Para mais informações, veja Pares de automação personalizados.
Outra API de layout
Existem algumas outras APIs que fazem parte do sistema de layout, mas não são declaradas pelo Panel. Você pode usá-los em uma implementação de painel ou em um controle personalizado que usa painéis.
- UpdateLayout, InvalidateMeasure e InvalidateArrange são métodos que iniciam um passo de layout. InvalidateArrange pode não acionar um passe de medida, mas os outros dois sim. Nunca chame esses métodos de dentro de uma substituição de método de layout, porque é quase certo que vão causar um loop de layout. O código controlador normalmente também não precisa chamá-los. A maioria dos aspetos do layout é acionada automaticamente pela deteção de alterações nas propriedades de layout definidas pela estrutura, como Largura e assim por diante.
- LayoutUpdated é um evento que é acionado quando algum aspeto do layout do elemento é alterado. Isso não é específico para painéis; o evento é definido por FrameworkElement.
- SizeChanged é um evento que é acionado somente depois que os passos de layout são finalizados e indica que ActualHeight ou ActualWidth foram alterados como resultado. Este é outro evento FrameworkElement. Há casos em que LayoutUpdated é acionado, mas SizeChanged não. Por exemplo, o conteúdo interno pode ser reorganizado, mas o tamanho do elemento não mudou.
Tópicos relacionados
Referência
Conceitos
Windows developer