Dans WPF, comment appliquer plusieurs styles à un FrameworkElement
? Par exemple, j'ai un contrôle qui a déjà un style. J'ai aussi un style à part que je voudrais y ajouter sans faire oublier le premier. Les styles ont différents TargetTypes, donc je ne peux pas simplement étendre l'un avec l'autre.
153
Réponses:
Je pense que la réponse simple est que vous ne pouvez pas faire (du moins dans cette version de WPF) ce que vous essayez de faire.
Autrement dit, pour un élément particulier, un seul style peut être appliqué.
Cependant, comme d'autres l'ont indiqué ci-dessus, vous pouvez peut-être utiliser
BasedOn
pour vous aider. Découvrez le morceau suivant de xaml en vrac. Vous y verrez que j'ai un style de base qui définit une propriété qui existe sur la classe de base de l'élément auquel je souhaite appliquer deux styles. Et, dans le deuxième style qui est basé sur le style de base, j'ai défini une autre propriété.Donc, l'idée ici ... est que si vous pouvez en quelque sorte séparer les propriétés que vous souhaitez définir ... en fonction de la hiérarchie d'héritage de l'élément sur lequel vous souhaitez définir plusieurs styles ... vous pourriez avoir une solution de contournement.
J'espère que cela t'aides.
Remarque:
Une chose en particulier à noter. Si vous changez le
TargetType
dans le second style (dans le premier ensemble de xaml ci-dessus) enButtonBase
, les deux styles ne sont pas appliqués. Cependant, consultez le xaml suivant ci-dessous pour contourner cette restriction. Fondamentalement, cela signifie que vous devez donner au style une clé et le référencer avec cette clé.la source
derivedStyle
doit venir après lebaseStyle
Bea Stollnitz a publié un bon article sur l'utilisation d'une extension de balisage pour cela, sous le titre "Comment puis-je définir plusieurs styles dans WPF?"
Ce blog est mort maintenant, donc je reproduis le message ici
WPF et Silverlight offrent tous deux la possibilité de dériver un style d'un autre style via la propriété «BasedOn». Cette fonctionnalité permet aux développeurs d'organiser leurs styles en utilisant une hiérarchie similaire à l'héritage de classe. Considérez les styles suivants:
Avec cette syntaxe, un Button qui utilise RedButtonStyle aura sa propriété Foreground définie sur Red et sa propriété Margin définie sur 10.
Cette fonctionnalité existe dans WPF depuis longtemps et elle est nouvelle dans Silverlight 3.
Que faire si vous souhaitez définir plusieurs styles sur un élément? Ni WPF ni Silverlight ne fournissent une solution à ce problème prête à l'emploi. Heureusement, il existe des moyens d'implémenter ce comportement dans WPF, dont je parlerai dans ce billet de blog.
WPF et Silverlight utilisent des extensions de balisage pour fournir des propriétés avec des valeurs qui nécessitent une certaine logique pour obtenir. Les extensions de balisage sont facilement reconnaissables par la présence d'accolades les entourant en XAML. Par exemple, l'extension de balisage {Binding} contient une logique permettant d'extraire une valeur d'une source de données et de la mettre à jour lorsque des modifications se produisent; l'extension de balisage {StaticResource} contient une logique permettant de récupérer une valeur dans un dictionnaire de ressources en fonction d'une clé. Heureusement pour nous, WPF permet aux utilisateurs d'écrire leurs propres extensions de balisage personnalisées. Cette fonctionnalité n'est pas encore présente dans Silverlight, donc la solution de ce blog n'est applicable qu'à WPF.
D'autres ont écrit d'excellentes solutions pour fusionner deux styles à l'aide d'extensions de balisage. Cependant, je voulais une solution qui offre la possibilité de fusionner un nombre illimité de styles, ce qui est un peu plus délicat.
L'écriture d'une extension de balisage est simple. La première étape consiste à créer une classe qui dérive de MarkupExtension et à utiliser l'attribut MarkupExtensionReturnType pour indiquer que vous souhaitez que la valeur renvoyée par votre extension de balisage soit de type Style.
Spécification des entrées de l'extension de balisage
Nous aimerions donner aux utilisateurs de notre extension de balisage un moyen simple de spécifier les styles à fusionner. Il existe essentiellement deux façons dont l'utilisateur peut spécifier des entrées dans une extension de balisage. L'utilisateur peut définir des propriétés ou transmettre des paramètres au constructeur. Étant donné que dans ce scénario, l'utilisateur a besoin de la possibilité de spécifier un nombre illimité de styles, ma première approche a été de créer un constructeur qui prend n'importe quel nombre de chaînes en utilisant le mot-clé «params»:
Mon objectif était de pouvoir écrire les entrées comme suit:
Notez la virgule séparant les différentes clés de style. Malheureusement, les extensions de balisage personnalisées ne prennent pas en charge un nombre illimité de paramètres de constructeur, donc cette approche entraîne une erreur de compilation. Si je savais à l'avance combien de styles je voulais fusionner, j'aurais pu utiliser la même syntaxe XAML avec un constructeur prenant le nombre de chaînes souhaité:
Pour contourner ce problème, j'ai décidé que le paramètre constructeur prenne une seule chaîne qui spécifie les noms de style séparés par des espaces. La syntaxe n'est pas trop mauvaise:
Calcul de la sortie de l'extension de balisage
Pour calculer la sortie d'une extension de balisage, nous devons remplacer une méthode de MarkupExtension appelée «provideValue». La valeur renvoyée par cette méthode sera définie dans la cible de l'extension de balisage.
J'ai commencé par créer une méthode d'extension pour Style qui sait fusionner deux styles. Le code de cette méthode est assez simple:
Avec la logique ci-dessus, le premier style est modifié pour inclure toutes les informations du second. S'il y a des conflits (par exemple, les deux styles ont un setter pour la même propriété), le second style l'emporte. Notez qu'en plus de copier les styles et les déclencheurs, j'ai également pris en compte les valeurs TargetType et BasedOn ainsi que toutes les ressources que le second style peut avoir. Pour le TargetType du style fusionné, j'ai utilisé le type le plus dérivé. Si le deuxième style a un style BasedOn, je fusionne sa hiérarchie de styles de manière récursive. S'il a des ressources, je les copie dans le premier style. Si ces ressources sont référencées à l'aide de {StaticResource}, elles sont résolues statiquement avant l'exécution de ce code de fusion, et il n'est donc pas nécessaire de les déplacer. J'ai ajouté ce code au cas où nous utiliserions DynamicResources.
La méthode d'extension illustrée ci-dessus active la syntaxe suivante:
Cette syntaxe est utile à condition que j'aie des instances des deux styles dans FournirValue. Eh bien, non. Tout ce que j'obtiens du constructeur est une liste de clés de chaîne pour ces styles. S'il y avait un support pour les paramètres dans les paramètres du constructeur, j'aurais pu utiliser la syntaxe suivante pour obtenir les instances de style réelles:
Mais ça ne marche pas. Et même si la limitation des paramètres n'existait pas, nous frapperions probablement une autre limitation des extensions de balisage, où nous devrions utiliser la syntaxe d'élément de propriété au lieu de la syntaxe d'attribut pour spécifier les ressources statiques, ce qui est verbeux et encombrant (j'explique cela bug mieux dans un précédent article de blog ). Et même si ces deux limitations n'existaient pas, je préférerais quand même écrire la liste des styles en utilisant uniquement leurs noms - elle est plus courte et plus simple à lire qu'une StaticResource pour chacun.
La solution consiste à créer une StaticResourceExtension à l'aide de code. Étant donné une clé de style de type string et un fournisseur de services, je peux utiliser StaticResourceExtension pour récupérer l'instance de style réelle. Voici la syntaxe:
Nous avons maintenant toutes les pièces nécessaires pour écrire la méthode provideValue:
Voici un exemple complet de l'utilisation de l'extension de balisage MultiStyle:
la source
Mais vous pouvez étendre d'un autre ... jetez un œil à la propriété BasedOn
la source
WPF / XAML ne fournit pas cette fonctionnalité de manière native, mais il fournit l'extensibilité pour vous permettre de faire ce que vous voulez.
Nous avons rencontré le même besoin et avons fini par créer notre propre extension de balisage XAML (que nous avons appelée "MergedStylesExtension") pour nous permettre de créer un nouveau style à partir de deux autres styles (qui, si nécessaire, pourraient probablement être utilisés plusieurs fois dans un ligne pour hériter d'encore plus de styles).
En raison d'un bogue WPF / XAML, nous devons utiliser la syntaxe d'élément de propriété pour l'utiliser, mais à part cela, cela semble fonctionner correctement. Par exemple,
J'ai récemment écrit à ce sujet ici: http://swdeveloper.wordpress.com/2009/01/03/wpf-xaml-multiple-style-inheritance-and-markup-extensions/
la source
Cela est possible en créant une classe d'assistance pour utiliser et encapsuler vos styles. CompoundStyle mentionné ici montre comment le faire. Il existe plusieurs façons, mais la plus simple est de faire ce qui suit:
J'espère que cela pourra aider.
la source
Utilisez
AttachedProperty
pour définir plusieurs styles comme le code suivant:Usege:
Résultat:
la source
si vous ne touchez à aucune propriété spécifique, vous pouvez obtenir toutes les propriétés de base et communes du style dont le type de cible serait FrameworkElement. ensuite, vous pouvez créer des saveurs spécifiques pour chaque type de cible dont vous avez besoin, sans avoir à copier à nouveau toutes ces propriétés communes.
la source
Vous pouvez probablement obtenir quelque chose de similaire si vous appliquez cela à une collection d'éléments à l'aide d'un StyleSelector, je l'ai utilisé pour aborder un problème similaire en utilisant différents styles sur TreeViewItems en fonction du type d'objet lié dans l'arborescence. Vous devrez peut-être modifier légèrement la classe ci-dessous pour vous adapter à votre approche particulière, mais j'espère que cela vous permettra de démarrer
Vous appliquez ensuite ceci comme tel
la source
Parfois, vous pouvez aborder cela en imbriquant des panneaux. Supposons que vous ayez un style qui change le premier plan et un autre change la taille de police, vous pouvez appliquer ce dernier sur un TextBlock, et le mettre dans une grille dont son style est le premier. Cela pourrait aider et pourrait être le moyen le plus simple dans certains cas, même si cela ne résoudra pas tous les problèmes.
la source
Lorsque vous remplacez SelectStyle, vous pouvez obtenir la propriété GroupBy via une réflexion comme ci-dessous:
la source
Si vous essayez d'appliquer un style unique à un seul élément en tant qu'addition à un style de base, il existe une manière complètement différente de le faire qui est à mon humble avis bien meilleure pour un code lisible et maintenable.
Il est extrêmement courant de devoir modifier les paramètres par élément individuel. Définir des styles de dictionnaire uniquement pour une utilisation sur un élément est extrêmement compliqué à maintenir ou à comprendre. Pour éviter de créer des styles uniquement pour des ajustements d'éléments ponctuels, lisez ma réponse à ma propre question ici:
https://stackoverflow.com/a/54497665/1402498
la source