mason
mason

Reputation: 32704

Show a notification window then fade it out, but pause the animation on mouse over

I'm normally an ASP.NET programmer, so I don't have much experience with WPF. I'm trying to create a notification window that opens on the screen in response to some server side event. I want it to behave similarly to the notifications given by Microsoft Outlook. The window should show for 5 seconds, then begin fading out which should last for 5 seconds, at which point the window should close. If the user mouses over the window, the windowTimer or the fade out animation should pause depending on how much time has elapsed. This works fine in the first 5 seconds of execution. However, once the fade out animation starts, it doesn't pause by mousing over. My question is: Why isn't my code pausing the animation when the mouse enters?

public partial class MainWindow : Window
{
    Timer windowTimer; //System.Timers.Timer
    AnimationClock clock;
    bool fadingOut = false;

    public MainWindow()
    {
        InitializeComponent();
    }

    protected override void OnContentRendered(EventArgs e)
    {
        base.OnContentRendered(e);
        windowTimer = new Timer(5000);//5 seconds
        windowTimer.Start();
        windowTimer.Elapsed += new ElapsedEventHandler(windowTimer_Elapsed);
    }

    protected override void OnMouseEnter(MouseEventArgs e)
    {
        base.OnMouseEnter(e);
        if (!fadingOut)
        {
            windowTimer.Stop();
        }
        if (clock != null)
        {
            clock.Controller.Pause();
        }
    }

    protected override void OnMouseLeave(MouseEventArgs e)
    {
        base.OnMouseLeave(e);
        if (!fadingOut)
        {
            windowTimer.Start();
        }
        if (clock != null)
        {
            clock.Controller.Resume();
        }
    }

    private void windowTimer_Elapsed(object sender, ElapsedEventArgs e)
    {
        windowTimer.Elapsed -= windowTimer_Elapsed;
        Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background,
            new Action(() => BeginFadeout()));
    }

    private void BeginFadeout()
    {
        windowTimer.Stop();
        fadingOut = true;
        DoubleAnimation fadeoutanim = new DoubleAnimation(0,
            (Duration)TimeSpan.FromSeconds(5));
        fadeoutanim.Completed += (s, _) => this.Close();
        clock = fadeoutanim.CreateClock();
        this.BeginAnimation(UIElement.OpacityProperty, fadeoutanim);
    }
}

Upvotes: 0

Views: 1092

Answers (2)

aegar
aegar

Reputation: 764

To get your code working you could just replace this.BeginAnimation(UIElement.OpacityProperty, fadeoutanim); with this.ApplyAnimationClock(OpacityProperty, clock);, otherwise the clock you get is not the one that controls the animation.

Also, your code will throw exception if the mouse is over the window when it starts, you should ensure that windowTimer is not null before starting / stopping it.

But using a storyboard is clearly a cleaner solution.

Instead of recreating your own notification, you should have a look at this library, which is available from NuGet and has a good howto.

Upvotes: 3

DotNetRussell
DotNetRussell

Reputation: 9857

So the easiest way to do this is with storyboards. Since blend is a very easy tool to use and makes quick work of this I will use this as the example. You can also of course create the storyboard by hand.

Step 1: Create your control

enter image description here

Step2: Create your storyboard by clicking the little plus symbol on the left. Name it something meaningful. You will note that the UI element you have selected in the list on the left will be the one that the storyboard is applied to.

enter image description here

Step 3: On the left set the keyframe. This is how long the animation will take to execute and where it will execute what you tell it to. Make sure to set the keyframe FIRST then set the properties.

enter image description here

Step 4: Finally go into visual studio and execute your storyboard. In this example the storyboard resides in the static resources. So I pull it out of the static resources and tell it to either begin or stop (which returns it to the start which in this case is opacity 1)

enter image description here

If you still want to do it by hand then add this to your notification window XAML

<Window.Resources>
    <Storyboard x:Key="FadeOutAnimation">
        <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="window">
            <EasingDoubleKeyFrame KeyTime="0:0:1.1" Value="0"/>
        </DoubleAnimationUsingKeyFrames>
    </Storyboard>
</Window.Resources>

I know a quick copy and paste seems faster but learning to use Blend will save you a ton of UI heart ache in the future.

Upvotes: 4

Related Questions