Wie würde ich in WPF mehrere Stile auf a anwenden FrameworkElement
? Zum Beispiel habe ich ein Steuerelement, das bereits einen Stil hat. Ich habe auch einen separaten Stil, den ich hinzufügen möchte, ohne den ersten wegzublasen. Die Stile haben unterschiedliche TargetTypes, daher kann ich nicht einfach einen miteinander erweitern.
153
Antworten:
Ich denke, die einfache Antwort ist, dass Sie (zumindest in dieser Version von WPF) nicht das tun können, was Sie versuchen.
Das heißt, für ein bestimmtes Element kann nur ein Stil angewendet werden.
Wie andere oben bereits erwähnt haben, können Sie möglicherweise
BasedOn
helfen. Schauen Sie sich das folgende Stück lose Xaml an. Darin sehen Sie, dass ich einen Basisstil habe, der eine Eigenschaft festlegt, die für die Basisklasse des Elements vorhanden ist, auf das ich zwei Stile anwenden möchte. Und im zweiten Stil, der auf dem Basisstil basiert, habe ich eine andere Eigenschaft festgelegt.Die Idee hier ... ist also, wenn Sie die Eigenschaften, die Sie festlegen möchten, irgendwie trennen können ... entsprechend der Vererbungshierarchie des Elements, für das Sie mehrere Stile festlegen möchten ... haben Sie möglicherweise eine Problemumgehung.
Hoffe das hilft.
Hinweis:
Eines ist besonders zu beachten. Wenn Sie den
TargetType
im zweiten Stil (im ersten Satz von xaml oben) in ändernButtonBase
, werden die beiden Stile nicht angewendet. Schauen Sie sich jedoch das folgende xaml an, um diese Einschränkung zu umgehen. Grundsätzlich bedeutet dies, dass Sie dem Stil einen Schlüssel geben und ihn mit diesem Schlüssel referenzieren müssen.quelle
derivedStyle
muss nach dembaseStyle
Bea Stollnitz hatte einen guten Blogbeitrag über die Verwendung einer Markup-Erweiterung unter der Überschrift "Wie kann ich mehrere Stile in WPF festlegen?"
Dieser Blog ist jetzt tot, also reproduziere ich den Beitrag hier
WPF und Silverlight bieten beide die Möglichkeit, einen Stil über die Eigenschaft „BasedOn“ von einem anderen Stil abzuleiten. Mit dieser Funktion können Entwickler ihre Stile mithilfe einer Hierarchie ähnlich der Klassenvererbung organisieren. Betrachten Sie die folgenden Stile:
Bei dieser Syntax wird für eine Schaltfläche, die RedButtonStyle verwendet, die Foreground-Eigenschaft auf Red und die Margin-Eigenschaft auf 10 festgelegt.
Diese Funktion gibt es in WPF schon lange und sie ist neu in Silverlight 3.
Was ist, wenn Sie mehr als einen Stil für ein Element festlegen möchten? Weder WPF noch Silverlight bieten eine sofort einsatzbereite Lösung für dieses Problem. Glücklicherweise gibt es Möglichkeiten, dieses Verhalten in WPF zu implementieren, die ich in diesem Blogbeitrag diskutieren werde.
WPF und Silverlight verwenden Markup-Erweiterungen, um Eigenschaften mit Werten zu versehen, für deren Abruf eine gewisse Logik erforderlich ist. Markup-Erweiterungen sind leicht an geschweiften Klammern in XAML zu erkennen. Beispielsweise enthält die Markup-Erweiterung {Binding} eine Logik zum Abrufen eines Werts aus einer Datenquelle und zum Aktualisieren, wenn Änderungen auftreten. Die Markup-Erweiterung {StaticResource} enthält Logik zum Abrufen eines Werts aus einem Ressourcenwörterbuch basierend auf einem Schlüssel. Zum Glück können Benutzer mit WPF ihre eigenen benutzerdefinierten Markup-Erweiterungen schreiben. Diese Funktion ist in Silverlight noch nicht vorhanden, daher gilt die Lösung in diesem Blog nur für WPF.
Andere haben großartige Lösungen geschrieben, um zwei Stile mithilfe von Markup-Erweiterungen zusammenzuführen. Ich wollte jedoch eine Lösung, die die Möglichkeit bietet, eine unbegrenzte Anzahl von Stilen zusammenzuführen, was etwas schwieriger ist.
Das Schreiben einer Markup-Erweiterung ist unkompliziert. Der erste Schritt besteht darin, eine Klasse zu erstellen, die von MarkupExtension abgeleitet ist, und das MarkupExtensionReturnType-Attribut zu verwenden, um anzugeben, dass der von Ihrer Markup-Erweiterung zurückgegebene Wert vom Typ Style sein soll.
Angeben von Eingaben für die Markup-Erweiterung
Wir möchten Benutzern unserer Markup-Erweiterung eine einfache Möglichkeit bieten, die zusammenzuführenden Stile anzugeben. Grundsätzlich gibt es zwei Möglichkeiten, wie der Benutzer Eingaben für eine Markup-Erweiterung angeben kann. Der Benutzer kann Eigenschaften festlegen oder Parameter an den Konstruktor übergeben. Da der Benutzer in diesem Szenario die Möglichkeit haben muss, eine unbegrenzte Anzahl von Stilen anzugeben, bestand mein erster Ansatz darin, einen Konstruktor zu erstellen, der eine beliebige Anzahl von Zeichenfolgen mit dem Schlüsselwort "params" verwendet:
Mein Ziel war es, die Eingaben wie folgt schreiben zu können:
Beachten Sie das Komma zwischen den verschiedenen Stilschlüsseln. Leider unterstützen benutzerdefinierte Markup-Erweiterungen keine unbegrenzte Anzahl von Konstruktorparametern, sodass dieser Ansatz zu einem Kompilierungsfehler führt. Wenn ich im Voraus gewusst hätte, wie viele Stile ich zusammenführen wollte, hätte ich dieselbe XAML-Syntax mit einem Konstruktor verwenden können, der die gewünschte Anzahl von Zeichenfolgen verwendet:
Um dieses Problem zu umgehen, habe ich beschlossen, dass der Konstruktorparameter eine einzelne Zeichenfolge verwendet, die die durch Leerzeichen getrennten Stilnamen angibt. Die Syntax ist nicht schlecht:
Berechnung der Ausgabe der Markup-Erweiterung
Um die Ausgabe einer Markup-Erweiterung zu berechnen, müssen Sie eine Methode aus MarkupExtension mit dem Namen "ProvideValue" überschreiben. Der von dieser Methode zurückgegebene Wert wird im Ziel der Markup-Erweiterung festgelegt.
Ich habe zunächst eine Erweiterungsmethode für Style erstellt, mit der zwei Stile zusammengeführt werden können. Der Code für diese Methode ist recht einfach:
Mit der obigen Logik wird der erste Stil so geändert, dass er alle Informationen aus dem zweiten enthält. Wenn es Konflikte gibt (z. B. haben beide Stile einen Setter für dieselbe Eigenschaft), gewinnt der zweite Stil. Beachten Sie, dass ich neben dem Kopieren von Stilen und Triggern auch die TargetType- und BasedOn-Werte sowie alle Ressourcen des zweiten Stils berücksichtigt habe. Für den TargetType des zusammengeführten Stils habe ich den Typ verwendet, der stärker abgeleitet ist. Wenn der zweite Stil einen BasedOn-Stil hat, füge ich seine Stilhierarchie rekursiv zusammen. Wenn es Ressourcen hat, kopiere ich sie in den ersten Stil. Wenn auf diese Ressourcen mit {StaticResource} verwiesen wird, werden sie statisch aufgelöst, bevor dieser Zusammenführungscode ausgeführt wird. Daher ist es nicht erforderlich, sie zu verschieben. Ich habe diesen Code hinzugefügt, falls wir DynamicResources verwenden.
Die oben gezeigte Erweiterungsmethode aktiviert die folgende Syntax:
Diese Syntax ist nützlich, vorausgesetzt, ich habe Instanzen beider Stile in ProvideValue. Nun, ich nicht. Alles, was ich vom Konstruktor bekomme, ist eine Liste von Zeichenfolgenschlüsseln für diese Stile. Wenn die Konstruktorparameter Parameter unterstützen würden, hätte ich die folgende Syntax verwenden können, um die tatsächlichen Stilinstanzen abzurufen:
Das geht aber nicht Und selbst wenn die Parameterbeschränkung nicht vorhanden wäre, würden wir wahrscheinlich eine andere Einschränkung der Markup-Erweiterungen treffen, bei der wir die Eigenschaftselementsyntax anstelle der Attributsyntax verwenden müssten, um die statischen Ressourcen anzugeben, die ausführlich und umständlich sind (ich erkläre dies Fehler besser in einem früheren Blog-Beitrag ). Und selbst wenn diese beiden Einschränkungen nicht existieren würden, würde ich die Liste der Stile lieber nur mit ihren Namen schreiben - sie ist kürzer und einfacher zu lesen als eine StaticResource für jeden.
Die Lösung besteht darin, eine StaticResourceExtension mithilfe von Code zu erstellen. Bei einem Stilschlüssel vom Typ Zeichenfolge und einem Dienstanbieter kann ich StaticResourceExtension verwenden, um die tatsächliche Stilinstanz abzurufen. Hier ist die Syntax:
Jetzt haben wir alle Teile, die zum Schreiben der ProvideValue-Methode benötigt werden:
Hier ist ein vollständiges Beispiel für die Verwendung der MultiStyle-Markup-Erweiterung:
quelle
Sie können aber auch eine andere erweitern. Schauen Sie sich die BasedOn-Eigenschaft an
quelle
WPF / XAML bietet diese Funktionalität nicht nativ, bietet jedoch die Erweiterbarkeit, mit der Sie tun können, was Sie möchten.
Wir hatten das gleiche Bedürfnis und haben schließlich unsere eigene XAML-Markup-Erweiterung (die wir "MergedStylesExtension" nannten) erstellt, damit wir aus zwei anderen Stilen einen neuen Stil erstellen können (der bei Bedarf wahrscheinlich mehrmals in einem verwendet werden kann Zeile, um von noch mehr Stilen zu erben).
Aufgrund eines WPF / XAML-Fehlers müssen wir die Eigenschaftselementsyntax verwenden, um es zu verwenden, aber ansonsten scheint es in Ordnung zu funktionieren. Z.B,
Ich habe kürzlich hier darüber geschrieben: http://swdeveloper.wordpress.com/2009/01/03/wpf-xaml-multiple-style-inheritance-and-markup-extensions/
quelle
Dies ist möglich, indem Sie eine Hilfsklasse erstellen, um Ihre Stile zu verwenden und zu verpacken. Der hier erwähnte CompoundStyle zeigt, wie es geht. Es gibt mehrere Möglichkeiten, aber die einfachste ist die folgende:
Hoffentlich hilft das.
quelle
Verwenden Sie
AttachedProperty
diese Option , um mehrere Stile wie den folgenden Code festzulegen:Verwendung:
Ergebnis:
quelle
Wenn Sie keine bestimmten Eigenschaften berühren, können Sie alle Basis- und allgemeinen Eigenschaften für den Stil abrufen, dessen Zieltyp FrameworkElement wäre. Anschließend können Sie für jeden gewünschten Zieltyp bestimmte Geschmacksrichtungen erstellen, ohne alle allgemeinen Eigenschaften erneut kopieren zu müssen.
quelle
Sie können wahrscheinlich etwas Ähnliches erhalten, wenn Sie dies mithilfe eines StyleSelector auf eine Sammlung von Elementen anwenden. Ich habe dies verwendet, um ein ähnliches Problem bei der Verwendung unterschiedlicher Stile in TreeViewItems zu lösen, abhängig vom gebundenen Objekttyp im Baum. Möglicherweise müssen Sie die unten stehende Klasse geringfügig ändern, um sie an Ihren speziellen Ansatz anzupassen. Hoffentlich können Sie damit beginnen
Sie wenden dies dann so an
quelle
Manchmal können Sie dies erreichen, indem Sie Panels verschachteln. Angenommen, Sie haben einen Stil, der den Vordergrund ändert, und einen anderen, der die Schriftgröße ändert. Sie können den letzteren auf einen TextBlock anwenden und ihn in ein Raster einfügen, dessen Stil der erste ist. Dies kann hilfreich sein und in einigen Fällen der einfachste Weg sein, obwohl nicht alle Probleme gelöst werden.
quelle
Wenn Sie SelectStyle überschreiben, können Sie die GroupBy-Eigenschaft über die folgende Reflektion abrufen:
quelle
Wenn Sie versuchen, einen eindeutigen Stil auf nur ein einzelnes Element anzuwenden als Ergänzung zu einem Basisstil , gibt es eine völlig andere Möglichkeit, die meiner Meinung nach für lesbaren und wartbaren Code viel besser ist.
Es ist sehr häufig erforderlich, Parameter pro einzelnem Element anzupassen. Das Definieren von Wörterbuchstilen nur für die Verwendung mit einem Element ist äußerst umständlich zu pflegen oder sinnvoll. Lesen Sie hier meine Antwort auf meine eigene Frage, um zu vermeiden, dass Stile nur für einmalige Elementänderungen erstellt werden:
https://stackoverflow.com/a/54497665/1402498
quelle