Benutzer mit den meisten Antworten
Style mit "Parameter"

Frage
-
Hallo!
Ich habe einen Basis Style (Slider_Switch) mit Werten, den ich mit anderen Parametern/Werten wieder "verwenden" möchte (Slider_Switch2). Deshalb habe ich die Parameter/Werte jeweils an die Resourcen des Styles gebunden.
Basis-Style:
<Style x:Key="Slider_Switch" TargetType="{x:Type CheckBox}"> <Style.Resources> <sys:Double x:Key="Slider_Switch_ContentWidth">70</sys:Double> ... </Style.Resources> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type CheckBox}"> <DockPanel x:Name="dockPanel"> <ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" Content="{TemplateBinding Content}" Width="{StaticResource Slider_Switch_ContentWidth}"/> ...
Abgeleiteter Style:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:sys="clr-namespace:System;assembly=mscorlib"> <!--Angepasste CheckBox ============================================================================================= Es wird ein Schiebeschalter mit fester Content-Breite simuliert. --> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="/Ressourcen/Slider_Switch.xaml" /> <!--Basis Resource "einbinden" --> </ResourceDictionary.MergedDictionaries> <Style x:Key="Slider_Switch2" TargetType="{x:Type CheckBox}" BasedOn="{StaticResource Slider_Switch}"> <Style.Resources> <!--Neue Werte für abgeleitete Resource setzen--> <sys:Double x:Key="Slider_Switch_ContentWidth">120</sys:Double>
... </Style.Resources> </Style> </ResourceDictionary>
Trotzdem werden bei der Anwendung der unterschiedlichen Styl's nur die Werte des Basis-Styls wirksam:
<CheckBox Content="Rahmen" Style="{StaticResource Slider_Switch2}" /> <CheckBox Content="Hintergrund:" Style="{StaticResource Slider_Switch }" />
Antworten
-
Hi,
Dein Problem hat mir keine Ruhe gelassen und ich habe eine Lösung gefunden, die aber etwas Code enthält. Anstelle die Breite im XAML zuzuweisen, mache ich das im Beispiel per Code.Die Ableitung eines Style mit BaseOn ist nicht mit einer Vererbung von Klassen vergleichbar. Ressourcen im abgeleiteten Style (BasedOn) überschreiben nicht die Ressourcen im Basis-Style. Und der Basis-Style holt sich immer die Ressourcen aus seiner Umgebung.
In meinem Beispiel hole ich mir einfach die Ressource des aktuellen Styles (Basis oder BasedOn) und trage den Wert in den ContentPresenter ein.
XAML:
<Window x:Class="Window28" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:sys="clr-namespace:System;assembly=mscorlib" xmlns:local="clr-namespace:WpfApp1" xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" mc:Ignorable="d" Name="wnd" Title="Window28" Height="300" Width="200"> <Window.Resources> <local:Window28VM x:Key="vm"/> <Style x:Key="Slider_Switch" TargetType="{x:Type CheckBox}"> <Style.Resources> <sys:Double x:Key="Slider_Switch_ContentWidth">100</sys:Double> </Style.Resources> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type CheckBox}"> <DockPanel x:Name="dockPanel"> <CheckBox DockPanel.Dock="Left"/> <ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" Content="{TemplateBinding Content}"> <i:Interaction.Behaviors> <local:Window28Behavior/> </i:Interaction.Behaviors> </ContentPresenter> </DockPanel> </ControlTemplate> </Setter.Value> </Setter> </Style> <Style x:Key="Slider_Switch2" TargetType="{x:Type CheckBox}" BasedOn="{StaticResource Slider_Switch}"> <Style.Resources> <!--Neue Werte für abgeleitete Resource setzen--> <sys:Double x:Key="Slider_Switch_ContentWidth">60</sys:Double> </Style.Resources> </Style> </Window.Resources> <StackPanel DataContext="{StaticResource vm}" Margin="10"> <CheckBox Content="Rahmen:" Style="{StaticResource Slider_Switch2}" /> <CheckBox Content="Hintergrund:" Style="{StaticResource Slider_Switch}" /> </StackPanel> </Window>
Dazu der Code:
Public Class Window28Behavior Inherits Behavior(Of ContentPresenter) Protected Overrides Sub OnAttached() Dim cb = FindCheckBox(AssociatedObject) Dim st = cb.Style AssociatedObject.Width = CType(st.Resources("Slider_Switch_ContentWidth"), Double) End Sub Private Function FindCheckBox(obj As DependencyObject) As CheckBox Debug.Print(obj.ToString) Dim parent = VisualTreeHelper.GetParent(obj) If parent Is Nothing Then Return Nothing If TypeOf parent Is CheckBox Then Return CType(parent, CheckBox) Return FindCheckBox(parent) End Function Protected Overrides Sub OnDetaching() End Sub End Class
Im Beispiel fehlen Fehlerbehandlung bei falscher Nutzung, ggf. Parametrierung usw.
--
Viele Grüsse
Peter Fleischer (ehem. MVP)
Meine Homepage mit Tipps und Tricks- Als Antwort vorgeschlagen Peter Fleischer Sonntag, 8. April 2018 18:44
- Als Antwort markiert perlfred Montag, 9. April 2018 12:29
Alle Antworten
-
Hi,
wie wäre damit?<Style x:Key="Slider_Switch2" TargetType="{x:Type CheckBox}" BasedOn="{StaticResource Slider_Switch}"> <Style.Resources> <!--Neue Werte für abgeleitete Resource setzen--> <sys:Double x:Key="Slider_Switch_ContentWidth">40</sys:Double> </Style.Resources> <Setter Property="Width" Value="{StaticResource Slider_Switch_ContentWidth}"/> </Style>
--
Viele Grüsse
Peter Fleischer (ehem. MVP)
Meine Homepage mit Tipps und Tricks -
Hallo Peter!
Danke für deine Antwort!
In meinem Beispiel z.B. möchte ich die Breite des ContentPresenter im Style2 ändern und nicht die Breite der ComboBox. Ich kann doch nicht den Style noch einmal aufbauen, nur um die (veränderten) Werte wieder neu zuzuordnen?
Der Style2 müsste doch zuerst die Werte seiner Resource finden? Denke ich da falsch???
- Bearbeitet perlfred Freitag, 6. April 2018 06:29
-
Hi,
Dein Problem hat mir keine Ruhe gelassen und ich habe eine Lösung gefunden, die aber etwas Code enthält. Anstelle die Breite im XAML zuzuweisen, mache ich das im Beispiel per Code.Die Ableitung eines Style mit BaseOn ist nicht mit einer Vererbung von Klassen vergleichbar. Ressourcen im abgeleiteten Style (BasedOn) überschreiben nicht die Ressourcen im Basis-Style. Und der Basis-Style holt sich immer die Ressourcen aus seiner Umgebung.
In meinem Beispiel hole ich mir einfach die Ressource des aktuellen Styles (Basis oder BasedOn) und trage den Wert in den ContentPresenter ein.
XAML:
<Window x:Class="Window28" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:sys="clr-namespace:System;assembly=mscorlib" xmlns:local="clr-namespace:WpfApp1" xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" mc:Ignorable="d" Name="wnd" Title="Window28" Height="300" Width="200"> <Window.Resources> <local:Window28VM x:Key="vm"/> <Style x:Key="Slider_Switch" TargetType="{x:Type CheckBox}"> <Style.Resources> <sys:Double x:Key="Slider_Switch_ContentWidth">100</sys:Double> </Style.Resources> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type CheckBox}"> <DockPanel x:Name="dockPanel"> <CheckBox DockPanel.Dock="Left"/> <ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" Content="{TemplateBinding Content}"> <i:Interaction.Behaviors> <local:Window28Behavior/> </i:Interaction.Behaviors> </ContentPresenter> </DockPanel> </ControlTemplate> </Setter.Value> </Setter> </Style> <Style x:Key="Slider_Switch2" TargetType="{x:Type CheckBox}" BasedOn="{StaticResource Slider_Switch}"> <Style.Resources> <!--Neue Werte für abgeleitete Resource setzen--> <sys:Double x:Key="Slider_Switch_ContentWidth">60</sys:Double> </Style.Resources> </Style> </Window.Resources> <StackPanel DataContext="{StaticResource vm}" Margin="10"> <CheckBox Content="Rahmen:" Style="{StaticResource Slider_Switch2}" /> <CheckBox Content="Hintergrund:" Style="{StaticResource Slider_Switch}" /> </StackPanel> </Window>
Dazu der Code:
Public Class Window28Behavior Inherits Behavior(Of ContentPresenter) Protected Overrides Sub OnAttached() Dim cb = FindCheckBox(AssociatedObject) Dim st = cb.Style AssociatedObject.Width = CType(st.Resources("Slider_Switch_ContentWidth"), Double) End Sub Private Function FindCheckBox(obj As DependencyObject) As CheckBox Debug.Print(obj.ToString) Dim parent = VisualTreeHelper.GetParent(obj) If parent Is Nothing Then Return Nothing If TypeOf parent Is CheckBox Then Return CType(parent, CheckBox) Return FindCheckBox(parent) End Function Protected Overrides Sub OnDetaching() End Sub End Class
Im Beispiel fehlen Fehlerbehandlung bei falscher Nutzung, ggf. Parametrierung usw.
--
Viele Grüsse
Peter Fleischer (ehem. MVP)
Meine Homepage mit Tipps und Tricks- Als Antwort vorgeschlagen Peter Fleischer Sonntag, 8. April 2018 18:44
- Als Antwort markiert perlfred Montag, 9. April 2018 12:29
-
Hallo Peter!
Vielen Dank! für die umfangreiche Mühe, die du dir sicher bei der Lösung meines Problems gemacht hast !!!!!!!
Ich habe die Lösung für C# umgeschrieben und nachvollzogen, klappt alles super!!!
Jetzt bin ich noch am erweitern deines Lösungsweges, da ich auch noch andere Werte, die z.B. im DockPanel gesetzt werden (Slider_Switch_TextAn ...), unterschiedlich setzen muss. Ich habe es erst einmal mit einem weiteren Behavior probiert, dass muss ich aber noch testen.
Insgesamt ist es sicher effektiver ein UserControl zu schreiben, aber es geht auch so!
Vielen Dank, du hast meine Sichtweise wieder einmal erweitert.
Fred.
Falls es auch andere interessiert, hier die C# Variante von Peters Lösung:
public class ContentPresenterBehavior : System.Windows.Interactivity.Behavior<ContentPresenter> { protected override void OnAttached() { CheckBox cb = FindCheckBox(AssociatedObject); // CheckBox Finden if (cb != null) // CheckBox gefunden? { Style st = cb.Style; // Style der CheckBox AssociatedObject.Width = ((double)(st.Resources["Slider_Switch_ContentWidth"])); // Breite setzen } } private CheckBox FindCheckBox(DependencyObject obj) { DependencyObject parent = VisualTreeHelper.GetParent(obj); // Übergeordnetes Objekt bestimmen. if (parent == null) return null; // Wenn es kein übergeordnetes Objekt gibt, abbrechen. if (parent.GetType() == typeof(CheckBox)) return (CheckBox)parent; // Wenn akt. Objekt eine CheckBox ist, diese zurückgeben. return FindCheckBox(parent); // Wenn DP-Objekt keine CheckBox war, Funktion rekursiv aufrufen } protected override void OnDetaching() { } }
<DockPanel x:Name="dockPanel"> <ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" Content="{TemplateBinding Content}" ContentStringFormat="{TemplateBinding ContentStringFormat}" ContentTemplate="{TemplateBinding ContentTemplate}" RecognizesAccessKey="True" VerticalAlignment="Center" Width="{StaticResource Slider_Switch_ContentWidth}"> <i:Interaction.Behaviors> <local:ContentPresenterBehavior/> </i:Interaction.Behaviors> </ContentPresenter> <Grid Margin="5,5,0,5" Width="{StaticResource Slider_Switch_SchalterBreite}" Background="#FFC0CCD9"> <TextBlock Text="{StaticResource Slider_Switch_TextAn}" TextWrapping="Wrap" FontWeight="Bold" FontSize="12" HorizontalAlignment="Right" Margin="0,0,3,0"/> <TextBlock HorizontalAlignment="Left" Margin="2,0,0,0" FontSize="12" FontWeight="Bold" Text="{StaticResource Slider_Switch_TextAus}" TextWrapping="Wrap"/>
- Bearbeitet perlfred Montag, 9. April 2018 14:23
-
Hi Fred,
es freut mich, dass es bei Dir auch funktioniert. Es hat mich eine Menge Gehirnschmalz gekostet und viel Spaß gemacht. Ich hätte Dir den Code auch gleich in C#.NET schreiben können. Du hattest aber nicht die gewünschte Sprache genannt.--
Viele Grüsse
Peter Fleischer (ehem. MVP)
Meine Homepage mit Tipps und Tricks -
Hallo Peter!
Da ich auch noch andere Werte des Styl's mit den jeweils aktuellen Werten der Resource(n) setzen möchte, habe ich jetzt den Behavior auf das oberste Objekt des ControlTemplate gesetzt und die Objekte, die durch Resourcen aktualisiert werden sollen, explizit benannt:
<ControlTemplate TargetType="{x:Type CheckBox}"> <DockPanel x:Name="dockPanel"> <ContentPresenter x:Name="cpSchalter" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" Content="{TemplateBinding Content}" ContentStringFormat="{TemplateBinding ContentStringFormat}" ContentTemplate="{TemplateBinding ContentTemplate}" RecognizesAccessKey="True" VerticalAlignment="Center" Width="{StaticResource Slider_Switch_ContentWidth}"> </ContentPresenter> <Grid x:Name="grdSchalter" Margin="5,5,0,5" Width="{StaticResource Slider_Switch_SchalterBreite}" Background="#FFC0CCD9"> <TextBlock x:Name="tblTextAn" Text="{StaticResource Slider_Switch_TextAn}" TextWrapping="Wrap" FontWeight="Bold" FontSize="12" HorizontalAlignment="Right" Margin="0,0,3,0"/> <TextBlock x:Name="tblTextAus" HorizontalAlignment="Left" Margin="2,0,0,0" FontSize="12" FontWeight="Bold" Text="{StaticResource Slider_Switch_TextAus}" TextWrapping="Wrap"/> ...
</Grid> <i:Interaction.Behaviors> <local:DockPanelBehavior /> </i:Interaction.Behaviors> </DockPanel>
In der Behandlungsroutine des Behavior's suche ich dann die benannten Objekte und weise ihnen die akt. Werte der Resource zu:
public class DockPanelBehavior : System.Windows.Interactivity.Behavior<DockPanel> { protected override void OnAttached() { CheckBox cb = FindCheckBox(AssociatedObject); // (Übergeordnete) CheckBox finden if (cb != null) // CheckBox gefunden? { Style st = cb.Style; // Style der CheckBox (mit den akt. Resource-Werten!) DockPanel dp = AssociatedObject; // Behavior-Objekt in DockPanel casten object obj; // Allgemeines Objekt zur Referenzierung der "benannten" Objekte // Werte der akt. Resource den "benanten" Objekten zuordnen obj = dp.FindName("cpSchalter"); if (obj != null) ((ContentPresenter)obj).Width = ((double)(st.Resources["Slider_Switch_ContentWidth"])); obj = dp.FindName("tblTextAn"); if (obj != null) ((TextBlock)obj).Text = ((string)(st.Resources["Slider_Switch_TextAn"])); obj = dp.FindName("tblTextAus"); if (obj != null) ((TextBlock)obj).Text = ((string)(st.Resources["Slider_Switch_TextAus"])); } } private CheckBox FindCheckBox(DependencyObject obj) { DependencyObject parent = VisualTreeHelper.GetParent(obj); // Übergeordnetes Objekt bestimmen. if (parent == null) return null; // Wenn es kein übergeordnetes Objekt gibt, abbrechen. if (parent.GetType() == typeof(CheckBox)) return (CheckBox)parent; // Wenn akt. Objekt eine CheckBox ist, diese zurückgeben. return FindCheckBox(parent); // Wenn DP-Objekt keine CheckBox war, Funktion rekursiv aufrufen } protected override void OnDetaching() { } }
So ist dank deines Gehirnschmalzes :-) eine flexible Lösung entstanden, mit der man Styl's ableiten kann und trotzdem unterschiedliche Werte, die in der Resource des abgeleitetem Styls definiert werden, wirksam werden können.
Nochmals vielen Dank für die Darstellung des Lösungsweges!
Fred.