4

I have a Style with a Storyboard and Triggers. The animation work nicely, but only once.

I have 2 Storyboards FadeIn and FadeOut. In the EnterActions I start the FadeIn animation and in the ExitActions the FadeOut animation. I start the whole animation in code with

TextBlock.StartFade = true;

When i debug the above code, with every hit StartFade is False (which is correct).

So what do i do wrong?

Here is the Style in XAML. FadingTextBlock is just a custom TextBlock with a StartFade dependency property.

<Style TargetType="{x:Type Controls:FadingTextBlock}">
    <Setter Property="Visibility" Value="Collapsed" />
    <Setter Property="StartFade" Value="False" />
    <Setter Property="Opacity" Value="1.0" />

    <Style.Resources>
        <Storyboard x:Key="FadeIn">
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(Controls:FadingTextBlock.Opacity)" Storyboard.TargetName="{x:Null}">
                <EasingDoubleKeyFrame KeyTime="0:0:0.0" Value="0.0" />
                <EasingDoubleKeyFrame KeyTime="0:0:1.5" Value="1.0" />
            </DoubleAnimationUsingKeyFrames>
            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Controls:FadingTextBlock.Visibility)" Storyboard.TargetName="{x:Null}">
                <DiscreteObjectKeyFrame KeyTime="0:0:0.0" Value="{x:Static Visibility.Visible}"/>
            </ObjectAnimationUsingKeyFrames>
            <BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="(Controls:FadingTextBlock.StartFade)" Storyboard.TargetName="{x:Null}">
                <DiscreteBooleanKeyFrame KeyTime="0:0:1.5" Value="False" />
            </BooleanAnimationUsingKeyFrames>
        </Storyboard>
        <Storyboard x:Key="FadeOut">
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(Controls:FadingTextBlock.Opacity)" Storyboard.TargetName="{x:Null}">
                <EasingDoubleKeyFrame KeyTime="0:0:0.0" Value="1.0" />
                <EasingDoubleKeyFrame KeyTime="0:0:1.5" Value="0.0" />
            </DoubleAnimationUsingKeyFrames>
            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Controls:FadingTextBlock.Visibility)" Storyboard.TargetName="{x:Null}">
                <DiscreteObjectKeyFrame KeyTime="0:0:1.5" Value="{x:Static Visibility.Collapsed}" />
            </ObjectAnimationUsingKeyFrames>
        </Storyboard>
    </Style.Resources>
    <Style.Triggers>
        <Trigger Property="StartFade" Value="True">
            <Trigger.EnterActions>
                <BeginStoryboard x:Name="In" Storyboard="{StaticResource FadeIn}" />                    
            </Trigger.EnterActions>
            <Trigger.ExitActions>
                <BeginStoryboard x:Name="Out" Storyboard="{StaticResource FadeOut}" />                    
            </Trigger.ExitActions>
        </Trigger>
    </Style.Triggers>
</Style>
MystyxMac
  • 1,532
  • 2
  • 14
  • 28

3 Answers3

4

You should stop the started storyboard(s) from playing in the exit action using the <StopStoryboard> action.

    <Trigger Property="StartFade" Value="True">
        <Trigger.EnterActions>
            <StopStoryBoard BeginStoryboardName="Out"/>
            <BeginStoryboard x:Name="In" Storyboard="{StaticResource FadeIn}" />                    
        </Trigger.EnterActions>
        <Trigger.ExitActions>
            <StopStoryBoard BeginStoryboardName="In"/>
            <BeginStoryboard x:Name="Out" Storyboard="{StaticResource FadeOut}" />                    
        </Trigger.ExitActions>
    </Trigger>
Myrtle
  • 5,761
  • 33
  • 47
  • I tried that, but then the animation is looping (in out in out in out ...). – MystyxMac Jan 31 '13 at 08:35
  • Correction... it's only fading in. The fade out animation is not working. When the opacity reaches 1, it starts the fade in animation again. and again. and again... – MystyxMac Jan 31 '13 at 08:55
  • @MystyxMac That might be another problem, your storyboard is looping. Try tuning these properties: `AutoReverse="True"` and `RepeatBehavior="Forever"` on the Animation – Myrtle Jan 31 '13 at 09:08
  • Thanks for your answers, but i ended up using a local animation in code. – MystyxMac Jan 31 '13 at 10:17
  • @MystyxMac If that was your solution to the problem, please post it as an answer and accept it. (For future searches by other users). – Myrtle Jan 31 '13 at 10:57
1

I ended up using a local animation in code.

Set the TextBlock Opacity to 0 in Xaml.

// Fading animation for the textblock to show that the settings are updated.
DoubleAnimation fadingAnimation = new DoubleAnimation();
fadingAnimation.From = 0;
fadingAnimation.To = 1;
fadingAnimation.Duration = new Duration(TimeSpan.FromSeconds(1.5));
fadingAnimation.AutoReverse = true;

UpdateMessage.BeginAnimation(TextBlock.OpacityProperty, fadingAnimation);
MystyxMac
  • 1,532
  • 2
  • 14
  • 28
0

I Implemented my own C# solution for this, based on @MystyxMac Extracted it to a blend behavior, which exposed a challenge..

Attempting to show the same notification twice in a row will not work since the dependency property changed callback will not be called..

I overcame this by obtaining the binding, clearing it and setting it again.

public class ShowFadingTextBehavior : System.Windows.Interactivity.Behavior<TextBlock>
{

    public static readonly DependencyProperty DurationProperty = DependencyProperty.Register(
      "Duration", typeof(TimeSpan), typeof(ShowFadingTextBehavior), new PropertyMetadata(TimeSpan.FromSeconds(5)));

    public TimeSpan Duration
    {
        get { return (TimeSpan)GetValue(DurationProperty); }
        set { SetValue(DurationProperty, value); }
    }

    public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
        "Text", typeof (string), typeof (ShowFadingTextBehavior), new PropertyMetadata("",OnTextChanged));

    private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {

        var b = (ShowFadingTextBehavior) d;
        var text = (string) e.NewValue;

        if(string.IsNullOrEmpty(text))
            return;

        b.Show(text);
    }

    private void Show(string text)
    {
        var textBlock = AssociatedObject;

        if(textBlock==null)
            return;

        textBlock.Text = text;

        if(textBlock.Visibility==Visibility.Visible)
            return;

        textBlock.Visibility = Visibility.Visible;

        var a = new DoubleAnimation
        {
            From = 1.0,
            To = 0.0,
            FillBehavior = FillBehavior.Stop,
            BeginTime = TimeSpan.FromSeconds(1),
            Duration = new Duration(Duration)
        };

        var storyboard = new Storyboard();

        storyboard.Children.Add(a);
        Storyboard.SetTarget(a, textBlock);
        Storyboard.SetTargetProperty(a, new PropertyPath(UIElement.OpacityProperty));

        storyboard.Completed += delegate
        {
            textBlock.Visibility = Visibility.Collapsed;
            textBlock.Opacity = 1.0;

            var binding = BindingOperations.GetBinding(this, TextProperty);
            if(binding==null)
                return;

            ClearValue(TextProperty);
            BindingOperations.SetBinding(this, TextProperty, binding);
        };

        storyboard.Begin();

    }

    public string Text
    {
        get { return (string) GetValue(TextProperty); }
        set { SetValue(TextProperty, value); }
    }
}
Eyal Perry
  • 2,255
  • 18
  • 26