none
Style mit "Parameter" RRS feed

  • 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 }" />
    

    Donnerstag, 5. April 2018 12:43

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
    Sonntag, 8. April 2018 18:43

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

    Donnerstag, 5. April 2018 20:14
  • 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
    Freitag, 6. April 2018 06:11
  • 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
    Sonntag, 8. April 2018 18:43
  • 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
    Montag, 9. April 2018 14:22
  • 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

    Montag, 9. April 2018 19:50
  • 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.

    Dienstag, 10. April 2018 09:58