Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inherited WPF Style with triggers doesn't work

I have a style that fades out a control with animation:

<Style x:Key="ExpireFadeStyle">
    <Style.Resources>
        <!--Change this in a derived style if required-->
        <sys:Double x:Key="FinalVal">0.25</sys:Double>
    </Style.Resources>
    <Style.Triggers>
        <DataTrigger Binding="{Binding IsExpired}" Value="True">
            <DataTrigger.EnterActions>
                <BeginStoryboard x:Name="ExpireAnimation">
                    <Storyboard>
                        <DoubleAnimation Storyboard.TargetProperty="Opacity"
                                From="1" To="{StaticResource FinalVal}" Duration="0:0:3" />
                    </Storyboard>
                </BeginStoryboard>
            </DataTrigger.EnterActions>
            <DataTrigger.ExitActions>
                <StopStoryboard BeginStoryboardName="ExpireAnimation" />
            </DataTrigger.ExitActions>
        </DataTrigger>
    </Style.Triggers>
</Style>

(The IsExpired property always exists in the DataContext of the control where the style is supposed to be used.)

When I use this style directly in a control, it all works fine:

<StackPanel Style="{StaticResource ExpireFadeStyle}">
    ...etc

But when I derive from this style, just as simple as

<Style x:Key="ExpireTextFadeStyle" BasedOn="{StaticResource ExpireFadeStyle}"/>

...and then use the derived style on the same control the same way, it doesn't work. (The intention is, of course, to change it a bit, particularly the FinalVal, but it has to work in a trivial case first).

The inherited style itself does seem to work: if I add some Setter to it, I see its effect. It just seems that Triggers are not inherited or simply don't work. How to work around it?

like image 513
Zeus Avatar asked Aug 04 '16 02:08

Zeus


1 Answers

That's really a good question. I created an example test application to check this out and I have faced exactly the same problem. In debug mode I checked the value of the Style property and it was correct (BasedOn was set correctly and BasedOn.Triggers contains your Trigger). Nevertheless, the Trigger doesn't fire up when needed.

I have found a solution that make this work, however it is not a perfect one... I defined an attached dependency property which copies the triggers when is set to true.

public class StyleTriggersInheritanceHelper
{
    public static readonly DependencyProperty AddInheritedTriggersProperty =
        DependencyProperty.RegisterAttached("AddInheritedTriggers", typeof(bool), typeof(FrameworkElement), new FrameworkPropertyMetadata(new PropertyChangedCallback(OnAddInheritedTriggers)));

    private static void OnAddInheritedTriggers(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        var b = e.NewValue as bool?;
        if (b.HasValue && b.Value)
        {
            FrameworkElement element = sender as FrameworkElement;
            if(element != null)
            {
                Style style = element.Style;
                if(style != null)
                {
                    Style baseStyle = element.Style.BasedOn;
                    Style newStyle = new Style() { BasedOn = style };
                    if(baseStyle != null)
                    {
                        foreach (var tr in style.Triggers)
                            newStyle.Triggers.Add(tr);
                        foreach (var tr in baseStyle.Triggers)
                            newStyle.Triggers.Add(tr);
                    }
                    element.Style = newStyle;
                }
            }
        } 
    }

    public static bool GetAddInheritedTriggers(DependencyObject obj)
    {
        return (bool)obj.GetValue(AddInheritedTriggersProperty);
    }

    public static void SetAddInheritedTriggers(DependencyObject obj, bool value)
    {
        obj.SetValue(AddInheritedTriggersProperty, value);
    }
}

And xaml looks as follows:

<local:ExpirdedControl IsExpired="{Binding IsExpired}" Style="{StaticResource InheritedStyle}" x:Name="ExpiredName" local:StyleTriggersInheritanceHelper.AddInheritedTriggers="True"/>

Note that AddInheritedTriggers attached property is set to true and then Triggers are copied. One important thing is that you have to set AddInheritedTriggers after applying the Style, otherwise it wouldn't work!

For me it works, however the solution is not elegant, so I would like to see a better try of solving this issue.

like image 169
bakala12 Avatar answered Sep 18 '22 17:09

bakala12