Search code examples
wpftriggerswpf-style

Style Trigger stops to work after direct SetValue


Setting BackgroundProperty directly breaks IsMouseOver Style Trigger. How to have possiblity to set BackgroundProperty directly and retain trigger to work after that?

XAML:

<Window x:Class="WpfTriggers.MainWindow"
        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"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <SolidColorBrush x:Key="OriginalBackground" Color="Blue"/>
        <SolidColorBrush x:Key="TemplateBackground" Color="Red"/>
        <Style TargetType="Label" >
            <Style.Setters>
                <Setter Property="Background" Value="{StaticResource OriginalBackground}"/>
                <Setter Property="Foreground" Value="White"/>
            </Style.Setters>
            <Style.Triggers>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter Property="Background" Value="{StaticResource TemplateBackground}"/>
                </Trigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>
    <Grid>
        <StackPanel>
            <Label Focusable="True" x:Name="label">Hi!</Label>
            <Button Click="Button_Click">Over</Button>
        </StackPanel>
    </Grid>
   </Window>

Code behind:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace WpfTriggers
{
    public partial class MainWindow : Window
    {
        public MainWindow() => InitializeComponent();    
        void Button_Click(object sender, RoutedEventArgs e) => label.SetValue(Control.BackgroundProperty, Brushes.Green);       
    }
}

Solution

  • We solved issue by copying style, temporally replacing it by clone with background setter and returning style back. This was made into production as behavior which imitates data trigger with fully custom dynamic binding.

        public static class extensions
    {
        /// <summary>
        /// Shallow clone of style.
        /// </summary>
        /// <param name="original">Style to clone.</param>
        /// <returns>Cloned style.</returns>
        public static Style ShallowClone(this Style original)
        {
            var cloned = new Style(original.TargetType, original.BasedOn);
            foreach (var s in original.Setters) cloned.Setters.Add(s);
            foreach (var t in original.Triggers) cloned.Triggers.Add(t);
            cloned.Resources = original.Resources;
            return cloned;
        }
    }
    
        Style old;
    
        private void Button_MouseEnter_5(object sender, System.Windows.Input.MouseEventArgs e)
        {
            old = labelWithMofidiableStyle.Style;
            Style style = old.ShallowClone();
            style.Setters.Add(new Setter { Property = Control.BackgroundProperty, Value = this.FindResource("DynamicBackground") });
            labelWithMofidiableStyle.Style = style;
        }
    
    
    
        private void Button_MouseLeave_5(object sender, System.Windows.Input.MouseEventArgs e)
        {
            labelWithMofidiableStyle.Style = old;
        }