Partager via


Instructions pour la conception de contrôles stylables

Ce document récapitule un ensemble de bonnes pratiques à prendre en compte lors de la conception d’un contrôle que vous envisagez d’être facilement stylable et templatable. Nous sommes arrivés à cet ensemble de bonnes pratiques par le biais d’un grand nombre d’essais et d’erreurs tout en travaillant sur les styles de contrôle de thème pour l’ensemble de contrôles WPF intégré. Nous avons appris que le succès du style dépend autant d’un modèle objet bien conçu que du style lui-même. Ce document concerne l’auteur du contrôle et non l’auteur du style.

Terminologie

« Style et création de modèles » font référence à la suite de technologies qui permettent à un auteur de contrôle de différer les aspects visuels du contrôle au style et au modèle du contrôle. Cette suite de technologies comprend :

  • Styles (notamment les accesseurs Set, les déclencheurs et les plans conceptuels).

  • Ressources.

  • Modèles de contrôle.

  • Modèles de données.

Pour obtenir une présentation de l’application d’un style et du templating, consultez Application d’un style et templating.

Avant de commencer : Comprendre votre contrôle

Avant de passer à ces instructions, il est important de comprendre et d’avoir défini l’utilisation courante de votre contrôle. L’application d’un style expose un ensemble de possibilités souvent non maîtrisé. Les contrôles écrits pour être utilisés en grande partie (dans de nombreuses applications, par de nombreux développeurs) sont confrontés au défi que le style peut être utilisé pour apporter des modifications importantes à l’apparence visuelle du contrôle. En fait, le contrôle stylé peut ne pas ressembler aux intentions premières de l’auteur du contrôle. Étant donné que la flexibilité offerte par le style est essentiellement sans limites, vous pouvez utiliser l’idée d’utilisation courante pour vous aider à étendre vos décisions.

Pour comprendre l’utilisation courante de votre contrôle, il est judicieux de réfléchir à la proposition de valeur du contrôle. Que apporte votre contrôle à la table qu’aucun autre contrôle ne peut offrir ? L’utilisation courante n’implique aucune apparence visuelle spécifique, mais plutôt la philosophie du contrôle et un ensemble raisonnable d’attentes quant à son utilisation. Cette compréhension vous permet de faire des hypothèses sur le modèle de composition et les comportements définis par le style du contrôle dans le cas courant. Par exemple, dans le cas de ComboBox,le fait de comprendre l’utilisation courante ne vous donne aucune information sur le fait que ComboBox a des angles arrondis, mais cela vous permettra de déterminer que ComboBox devrait probablement avoir une fenêtre contextuelle et un moyen de basculer entre l’ouverture et la fermeture de celle-ci.

Instructions générales

  • N’appliquez pas strictement les contrats de modèle. Le contrat de modèle d’un contrôle peut se composer d’éléments, de commandes, de liaisons, de déclencheurs ou même de paramètres de propriété requis ou attendus pour qu’un contrôle fonctionne correctement.

    • Réduisez les contrats autant que possible.

    • Conception autour de l’attente que pendant le temps de conception (autrement dit, lors de l’utilisation d’un outil de conception), il est courant qu’un modèle de contrôle soit dans un état incomplet. WPF n’offre pas une infrastructure d’état de « composition », donc les contrôles doivent être générés avec l’idée que cet état peut être valide.

    • Ne lèvez pas d’exceptions quand aucun aspect d’un contrat de modèle n’est suivi. Dans le même état d’esprit, les panneaux ne doivent pas lever d’exceptions s’ils ont trop ou trop peu d’enfants.

  • Factoriser les fonctionnalités périphériques dans les éléments d’assistance de modèle. Chaque contrôle doit se concentrer sur ses fonctionnalités principales et sa proposition de valeur réelle et définie par l’utilisation courante du contrôle. À cette fin, utilisez des éléments de composition et d’assistance dans le modèle pour activer les comportements et visualisations périphériques, c’est-à-dire ces comportements et visualisations qui ne contribuent pas aux fonctionnalités principales du contrôle. Les éléments d’assistance se répartissent en trois catégories :

    • Les types d’assistance autonomes sont des contrôles publics et réutilisables ou des primitives utilisées de manière « anonyme » dans un modèle, ce qui signifie que ni l’élément d’assistance ni le contrôle stylé n’est conscient de l’autre. Techniquement, n’importe quel élément peut être un type anonyme, mais dans ce contexte, le terme décrit ces types qui encapsulent des fonctionnalités spécialisées pour activer des scénarios ciblés.

    • Les éléments d’assistance basés sur le type sont des nouveaux types qui encapsulent des fonctionnalités spéciales. Ces éléments sont généralement conçus avec une plage de fonctionnalités plus étroite que les contrôles ou primitives courants. Contrairement aux éléments d’assistance autonomes, les éléments d’assistance basés sur le type sont conscients du contexte dans lequel ils sont utilisés et doivent généralement partager des données avec le contrôle auquel ils appartiennent.

    • Les éléments d’assistance nommés sont des primitives ou des contrôles courants qu’un contrôle s’attend à trouver dans son modèle par nom. Ces éléments reçoivent un nom bien connu dans le modèle, ce qui permet à un contrôle de trouver l’élément et d’interagir avec lui par programmation. Il ne peut y avoir qu’un seul élément portant un nom donné dans n’importe quel modèle.

    Le tableau suivant présente les éléments d’assistance utilisés par les styles de contrôle aujourd’hui (cette liste n’est pas exhaustive) :

    Élément Catégorie Utilisé par
    ContentPresenter Basé sur les types Button, CheckBox, RadioButton, Frame, et ainsi de suite (tous les types ContentControl)
    ItemsPresenter Basé sur les types ListBox, ComboBox, Menu, et ainsi de suite (tous les types ItemsControl)
    ToolBarOverflowPanel Nommé ToolBar
    Popup Indépendant ComboBox, ToolBar, Menu, ToolTip, et ainsi de suite
    RepeatButton Nommé Slider, ScrollBar, et ainsi de suite
    ScrollBar Nommé ScrollViewer
    ScrollViewer Indépendant ListBox, ComboBox, Menu, Frame, et ainsi de suite
    TabPanel Indépendant TabControl
    TextBox Nommé ComboBox
    TickBar Basé sur les types Slider
  • Réduire les liaisons ou les paramètres de propriété spécifiés et requis par l’utilisateur sur les éléments d'aide. Il est courant qu’un élément d’assistance nécessite certaines liaisons ou paramètres de propriété pour fonctionner correctement dans le modèle de contrôle. L’élément d’assistance et le contrôle basé sur des modèles doivent, autant que possible, établir ces paramètres. Lorsque vous définissez des propriétés ou établissez des liaisons, veillez à ne pas remplacer les valeurs définies par l’utilisateur. Les meilleures pratiques spécifiques sont les suivantes :

    • Les éléments d’assistance nommés doivent être identifiés par le parent et le parent doit établir les paramètres requis sur l’élément d’assistance.

    • Les éléments d’assistance basés sur le type doivent établir les paramètres requis directement sur eux-mêmes. Cela peut nécessiter que l’élément d’assistance interroge le contexte d’information dans lequel il est utilisé, y compris son TemplatedParent (le type de contrôle du modèle dans lequel il est utilisé). Par exemple, ContentPresenter lie automatiquement la propriété Content de son TemplatedParent à sa propriété Content lorsqu’elle est utilisée dans un type dérivé ContentControl.

    • Les éléments d’assistance autonomes ne peuvent pas être optimisés de cette façon, car, par définition, ni l’élément d’assistance ni le parent ne connaissent l’autre.

  • Utilisez la propriété Name pour marquer des éléments au sein d’un modèle. Un contrôle qui doit trouver un élément dans son style afin de l’accéder par programme doit le faire à l’aide de la propriété Name et du paradigme FindName. Un contrôle ne doit pas lever d’exception lorsqu’un élément n’est pas trouvé, mais en mode silencieux et désactive correctement les fonctionnalités requises par cet élément.

  • Utilisez les meilleures pratiques pour exprimer l’état et le comportement du contrôle dans un style. Voici une liste triée des meilleures pratiques pour exprimer les changements d'état et les comportements de contrôle dans un style donné. Vous devez utiliser le premier élément de la liste qui active votre scénario.

    1. Liaison de propriété. Exemple : liaison entre ComboBox.IsDropDownOpen et ToggleButton.IsChecked.

    2. Déclenchement des modifications de propriété ou des animations de propriété. Exemple : état de pointage d’un Button.

    3. Commande. Exemple : LineUpCommand / LineDownCommand dans ScrollBar.

    4. Éléments d’assistance autonomes. Exemple : TabPanel dans TabControl.

    5. Types d’assistance basés sur le type. Exemple : ContentPresenter dans Button, TickBar dans Slider.

    6. Éléments d’assistance nommés. Exemple : TextBox dans ComboBox.

    7. Événements propagés à partir de types d’assistance nommés. Si vous écoutez des événements propagés à partir d’un élément de style, vous devez demander que l’élément générant l’événement puisse être identifié de manière unique. Exemple : Thumb dans ToolBar.

    8. Comportement OnRender personnalisé. Exemple : ButtonChrome dans Button.

  • Utiliser des déclencheurs de style (par opposition aux déclencheurs de modèle) avec parcimonie. Les déclencheurs qui affectent les propriétés sur les éléments du modèle doivent être déclarés dans le modèle. Les déclencheurs qui affectent les propriétés du contrôle (aucun TargetName) peuvent être déclarés dans le style, sauf si vous savez que la modification du modèle doit également détruire le déclencheur.

  • Soyez cohérent avec les modèles de style existants. Plusieurs fois, il existe plusieurs façons de résoudre un problème. Soyez conscient des modèles de style de contrôle existants et, dans la mesure du possible, soyez cohérent avec eux. Cela est particulièrement important pour les contrôles qui dérivent du même type de base (par exemple, ContentControl, ItemsControl, RangeBase, etc.).

  • Exposez des propriétés pour activer des scénarios de personnalisation courants sans recréer un modèle. WPF ne prend pas en charge les composants enfichables/personnalisables. Par conséquent, un utilisateur de contrôle ne dispose que de deux méthodes de personnalisation : définir des propriétés directement ou définir des propriétés à l’aide de styles. Dans cet esprit, il est approprié d’exposer un nombre limité de propriétés ciblées sur des scénarios de personnalisation très courants et à priorité élevée, qui nécessiteraient autrement la retemplatation. Voici les meilleures pratiques pour savoir quand et comment activer des scénarios de personnalisation :

    • Les personnalisations très courantes doivent être exposées en tant que propriétés sur le contrôle et consommées par le modèle.

    • Les personnalisations moins courantes (mais pas rares) doivent être exposées en tant que propriétés jointes et consommées par le modèle.

    • Il est acceptable que les personnalisations connues mais rares nécessitent une retemplatation.

Considérations relatives au thème

  • Les styles de thème doivent, autant que possible, posséder une sémantique des propriétés cohérente entre tous les thèmes, sans aucune garantie. Dans le cadre de sa documentation, votre contrôle doit avoir un document décrivant la sémantique des propriétés du contrôle, autrement dit, la « signification » d’une propriété pour un contrôle. Par exemple, le contrôle ComboBox doit définir la signification de la propriété Background dans ComboBox. Les styles par défaut de votre contrôle doivent tenter de suivre la sémantique définie dans ce document sur tous les thèmes. Les utilisateurs de contrôle doivent, d'autre part, savoir que la sémantique des propriétés peut varier d'un thème à l'autre. Dans certains cas, une propriété donnée peut ne pas être expressible sous les contraintes visuelles requises par un thème particulier. (Le thème Classique, par exemple, n’a pas de bordure simple à laquelle Thickness peut être appliqué à de nombreux contrôles.)

  • Les styles des thèmes n’ont pas besoin d’avoir une logique de déclenchement cohérente entre tous les thèmes. Le comportement exposé par un style de contrôle par le biais de déclencheurs ou d’animations peut varier d’un thème à un thème. Les utilisateurs de contrôle doivent savoir qu’un contrôle n’utilise pas nécessairement le même mécanisme pour obtenir un comportement particulier sur tous les thèmes. Un thème, par exemple, peut utiliser une animation pour exprimer le comportement de pointage où un autre thème utilise un déclencheur. Cela peut entraîner des incohérences dans la conservation du comportement sur les contrôles personnalisés. (La modification de la propriété d’arrière-plan, par exemple, peut ne pas affecter l’état de pointage du contrôle si cet état est exprimé à l’aide d’un déclencheur. Toutefois, si l’état du pointage est implémenté à l’aide d’une animation, la modification en arrière-plan peut interrompre irréparablement l’animation et, par conséquent, la transition d’état.)

  • Les styles de thème n'ont pas besoin d'avoir une sémantique de "mise en page" cohérente sur tous les thèmes. Par exemple, le style par défaut n’a pas besoin de garantir qu’un contrôle occupera la même taille dans tous les thèmes ou garantit qu’un contrôle aura les mêmes marges de contenu/ remplissage sur tous les thèmes.

Voir aussi