Compartilhar via


Diretrizes para criar controles estilizáveis

Este documento resume um conjunto de práticas recomendadas a serem consideradas ao projetar um controle que você pretende ser facilmente estilizável e templatável. Chegamos a esse conjunto de práticas recomendadas por meio de muita tentativa e erro durante o desenvolvimento dos estilos de controle de tema para o conjunto de controle interno do WPF. Aprendemos que o estilo bem-sucedido é tanto uma função de um modelo de objeto bem projetado quanto do próprio estilo. O público-alvo pretendido para este documento é o autor do controle, não o autor do estilo.

Terminologia

"Estilo e modelagem" referem-se ao conjunto de tecnologias que permitem que um autor de controle adie os aspectos visuais do controle para o estilo e o modelo do controle. Este conjunto de tecnologias inclui:

  • Estilos (incluindo definidores de propriedades, gatilhos e storyboards).

  • Recursos.

  • Modelos de controle.

  • Modelos de dados.

Para obter uma introdução ao estilo e à modelagem, consulte Estilo e Modelagem.

Antes de começar: entendendo seu controle

Antes de entrar nessas diretrizes, é importante entender e definir o uso comum do controle. O estilo expõe um conjunto muitas vezes indisciplinado de possibilidades. Os controles que são desenvolvidos para ampla utilização (em muitos aplicativos, por muitos desenvolvedores) enfrentam o desafio de que a estilização pode ser aplicada para fazer modificações significativas na aparência visual dos controles. Na verdade, o controle estilizado pode nem se assemelhar às intenções do autor do controle. Como a flexibilidade oferecida pelo estilo é essencialmente ilimitada, você pode usar a ideia de uso comum para ajudá-lo a definir o escopo de suas decisões.

Para entender o uso comum do controle, é bom pensar na proposta de valor do controle. O que seu controle apresenta que nenhum outro controle pode oferecer? O uso comum não implica nenhuma aparência visual específica, mas sim a filosofia do controle e um conjunto razoável de expectativas sobre seu uso. Esse entendimento permite que você faça algumas suposições sobre o modelo de composição e os comportamentos definidos pelo estilo do controle no caso comum. No caso de ComboBox, por exemplo, entender o uso comum não lhe dará nenhuma compreensão sobre se um determinado ComboBox tem cantos arredondados, mas lhe dará uma compreensão do fato de que o ComboBox provavelmente precisa de uma janela pop-up e alguma maneira de alternar entre aberto e fechado.

Diretrizes gerais

  • Não imponha estritamente contratos de modelo. O contrato de modelo de um controle pode consistir em elementos, comandos, associações, gatilhos ou até mesmo configurações de propriedade necessárias ou esperadas para que um controle funcione corretamente.

    • Minimize os contratos o máximo possível.

    • Projete em torno da expectativa de que durante o tempo de design (ou seja, ao usar uma ferramenta de design) é comum que um modelo de controle esteja em um estado incompleto. O WPF não oferece uma infraestrutura de estado de "composição", portanto, os controles precisam ser criados com a expectativa de que esse estado possa ser válido.

    • Não gere exceções quando qualquer aspecto de um contrato de modelo não for seguido. Nesse sentido, os painéis não devem gerar exceções se tiverem muitos ou poucos filhos.

  • Incorporar a funcionalidade periférica em elementos auxiliares de modelo. Cada controle deve ser focado em sua funcionalidade principal e na proposta de valor verdadeiro e definido pelo uso comum do controle. Para esse fim, use elementos auxiliares e de composição dentro do modelo para habilitar comportamentos periféricos e visualizações, ou seja, esses comportamentos e visualizações que não contribuem para a funcionalidade principal do controle. Os elementos auxiliares se enquadram em três categorias:

    • Tipos auxiliares autônomos são controles públicos e reutilizáveis ou primitivos que são usados "anonimamente" em um modelo, o que significa que nem o elemento auxiliar nem o controle com estilo estão cientes do outro. Tecnicamente, qualquer elemento pode ser um tipo anônimo, mas nesse contexto o termo descreve os tipos que encapsulam a funcionalidade especializada para habilitar cenários direcionados.

    • Elementos auxiliares baseados em tipos são novos tipos que encapsulam funcionalidades especializadas. Esses elementos normalmente são projetados com um intervalo mais estreito de funcionalidade do que controles comuns ou primitivos. Ao contrário dos elementos auxiliares autônomos, os elementos auxiliares baseados em tipos estão cientes do contexto em que são usados e normalmente devem compartilhar dados com o controle ao qual pertence o modelo.

    • Elementos auxiliares nomeados são controles comuns ou primitivos que um controle espera encontrar em seu modelo pelo nome. Esses elementos recebem um nome conhecido dentro do modelo, possibilitando que um controle localize o elemento e interaja com ele programaticamente. Só pode haver um elemento com um determinado nome em qualquer modelo.

    A tabela a seguir mostra elementos auxiliares empregados por estilos de controle hoje (esta lista não é exaustiva):

    Elemento Tipo Usado por
    ContentPresenter Baseado em tipo Button, CheckBox, RadioButton, Framee assim por diante (todos os ContentControl tipos)
    ItemsPresenter Baseado em tipo ListBox, ComboBox, Menue assim por diante (todos os ItemsControl tipos)
    ToolBarOverflowPanel Nomeado ToolBar
    Popup Autônomo ComboBox, ToolBar, Menu, ToolTipe assim por diante
    RepeatButton Nomeado Slider, ScrollBare assim por diante
    ScrollBar Nomeado ScrollViewer
    ScrollViewer Autônomo ListBox, ComboBox, Menu, Framee assim por diante
    TabPanel Autônomo TabControl
    TextBox Nomeado ComboBox
    TickBar Baseado em tipo Slider
  • Minimize as associações ou as configurações de propriedade especificadas pelo usuário necessárias em elementos auxiliares. É comum que um elemento auxiliar exija determinadas associações ou configurações de propriedade para funcionar corretamente dentro do modelo de controle. O elemento auxiliar e o controle modelo devem, tanto quanto possível, estabelecer essas configurações. Ao definir propriedades ou estabelecer associações, deve-se tomar cuidado para não substituir os valores definidos pelo usuário. As práticas recomendadas específicas são as seguintes:

    • Os elementos auxiliares nomeados devem ser identificados pelo pai e o pai deve estabelecer as configurações necessárias no elemento auxiliar.

    • Os elementos auxiliares baseados em tipo devem estabelecer as configurações necessárias diretamente em si mesmos. Isso pode exigir que o elemento auxiliar consulte o contexto informacional no qual está sendo usado, incluindo seu TemplatedParent (o tipo de controle do modelo no qual está sendo usado). Por exemplo, ContentPresenter associa automaticamente a propriedade Content do TemplatedParent à propriedade Content quando usada em um tipo derivado de ContentControl.

    • Elementos auxiliares autônomos não podem ser otimizados dessa forma porque, por definição, nem o elemento auxiliar nem o pai sabem sobre o outro.

  • Use a propriedade Name para sinalizar elementos dentro de um modelo. Um controle que precisa encontrar um elemento em seu estilo para acessá-lo programaticamente deve fazer isso usando a Name propriedade e o FindName paradigma. Um controle não deve gerar uma exceção quando um elemento não é encontrado, mas silenciosamente e normalmente desabilitar a funcionalidade que exigia esse elemento.

  • Use as práticas recomendadas para expressar o estado e o comportamento do controle em um estilo. Veja a seguir uma lista ordenada de práticas recomendadas para expressar as mudanças de estado de controle e comportamento de maneira estilística. Você deve usar o primeiro item na lista que habilita seu cenário.

    1. Vinculação de propriedade. Exemplo: associação entre ComboBox.IsDropDownOpen e ToggleButton.IsChecked.

    2. Alterações de propriedades ativadas ou animações de propriedades. Exemplo: o estado de focalização de um Button.

    3. Comando. Exemplo: LineUpCommand / LineDownCommand em ScrollBar.

    4. Elementos auxiliares autônomos. Exemplo: TabPanel em TabControl.

    5. Tipos auxiliares baseados em tipo. Exemplo: ContentPresenter in Button, TickBar in Slider.

    6. Elementos auxiliares nomeados. Exemplo: TextBox em ComboBox.

    7. Eventos em bolhas de tipos auxiliares nomeados. Se você ouvir eventos em bolhas de um elemento de estilo, deverá exigir que o elemento que gera o evento possa ser identificado exclusivamente. Exemplo: Thumb em ToolBar.

    8. Comportamento personalizado OnRender . Exemplo: ButtonChrome em Button.

  • Use gatilhos de estilo (em vez de gatilhos de modelo) com moderação. Gatilhos que afetam propriedades em elementos no modelo devem ser declarados no modelo. Gatilhos que afetam propriedades no controle (não TargetName) podem ser declarados no estilo, a menos que você saiba que alterar o template também deve invalidar o gatilho.

  • Seja consistente com os padrões de estilo existentes. Muitas vezes há várias maneiras de resolver um problema. Esteja ciente dos padrões de estilo de controle existentes e, quando possível, seja consistente com eles. Isso é especialmente importante para controles que derivam do mesmo tipo base (por exemplo, ContentControl, ItemsControle RangeBaseassim por diante).

  • Expor propriedades para habilitar cenários comuns de personalização sem remodelagem. O WPF não dá suporte a partes conectáveis/personalizáveis, portanto, um usuário de controle fica com apenas dois métodos de personalização: definir propriedades diretamente ou definir propriedades usando estilos. Com isso em mente, é apropriado apresentar um número limitado de propriedades direcionadas a cenários de personalização muito comuns e de alta prioridade, o que de outra forma exigiria o retemplateamento. Aqui estão as práticas recomendadas para quando e como habilitar cenários de personalização:

    • Customizações muito comuns devem ser expostas como propriedades no controle e consumidas pelo template.

    • Personalizações menos comuns (embora não raras) devem ser expostas como propriedades anexadas e consumidas pelo modelo.

    • É aceitável que personalizações conhecidas, mas raras, exijam reformulação.

Considerações sobre tema

  • Os estilos de temas devem tentar ter semântica consistente de propriedade em todos os temas, mas não oferecem qualquer garantia. Como parte de sua documentação, seu controle deve ter um documento que descreva a semântica das propriedades do controle, isto é, o "significado" de uma propriedade para um controle. Por exemplo, o ComboBox controle deve definir o significado da Background propriedade dentro ComboBox. Os estilos padrão do controle devem tentar seguir a semântica definida nesse documento em todos os temas. Os usuários de controle, por outro lado, devem estar cientes de que a semântica de propriedade pode mudar de tema para tema. Em determinados casos, uma determinada propriedade pode não ser expressível sob as restrições visuais exigidas por um tema específico. (O tema Clássico, por exemplo, não tem uma única borda à qual Thickness pode ser aplicada para muitos controles.)

  • Os estilos de tema não precisam ter semântica de gatilho consistente em todos os temas. O comportamento exposto por um estilo de controle por meio de gatilhos ou animações pode variar de tema para tema. Os usuários de controle devem estar cientes de que um controle não necessariamente empregará o mesmo mecanismo para alcançar um comportamento específico em todos os temas. Um tema, por exemplo, pode usar uma animação para expressar o comportamento ao passar o mouse, enquanto outro tema usa um gatilho. Isso pode resultar em inconsistências na preservação do comportamento em controles personalizados. (Alterar a propriedade em segundo plano, por exemplo, pode não afetar o estado de foco do controle se esse estado for expresso usando um gatilho. No entanto, se o estado de foco for implementado usando uma animação, a alteração em segundo plano poderá interromper irreparavelmente a animação e, portanto, a transição de estado.)

  • Os estilos de tema não precisam ter semântica de "layout" consistente em todos os temas. Por exemplo, o estilo padrão não precisa garantir que um controle ocupará a mesma quantidade de tamanho em todos os temas ou garantirá que um controle terá as mesmas margens de conteúdo/preenchimento em todos os temas.

Consulte também