Reputation: 300
I have a WPF slide notification I want to show in the bottom right corner of the screen. I need to show and hide the notification multiple times from the main program, so I cannot Close() and Show() the window each time. I have to keep doing Hide() and Show().
IsHitTestVisible is set to False so that I can click anywhere on the window to dismiss and hide the notification and bring another window to the foreground.
The very first time I execute the window animation, it runs perfectly. If I let the window animation run all the way to the end, then the next time the slide notification is shown, the animation is displayed perfectly.
The problem comes in if I click on the window before the animation has run it's full course. Clicking on the window hides the window and then the storyboard is set to SkipStoryboardtoFill and FillBehavior is set to Stop so that the animation clocks get reset to their original values. However, the next time the hidden notification window is shown, it seems to remember the last window position the window had before it was hidden, and then starts the animation. So the animation looks really strange. I am trying to figure out how to set up the animation so that it displays the same way each time without this glitch.
<Window
x:Class="NotificationWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Notification Popup"
Width="480"
Height="140"
WindowStyle="None"
AllowsTransparency="True"
Background="Transparent"
>
<Grid Name="ToastWindowGrid" RenderTransformOrigin="0,1">
<Border Name="ToastWindowBorder" BorderThickness="0" Background="#333333">
<StackPanel Name="ToastWindowStackPanel" Margin="10" Orientation="Horizontal">
<Image Name="ToastLogo" Width="100" Height="100" Source="D:\Development\resources\WindowsLogo-Blue-x100.png"/>
<StackPanel Name="ToastMessageStackPanel" Width="359">
<TextBox Name="ToastTitleTextBox" Margin="5" MaxWidth="340" Background="#333333" BorderThickness="0" IsReadOnly="True" Foreground="White" FontSize="20" Text="Windows 10 Upgrade" FontWeight="Bold" HorizontalContentAlignment="Center" Width="Auto" HorizontalAlignment="Stretch" IsHitTestVisible="False"/>
<TextBox Name="TotastMessageTextBox" Margin="5" MaxWidth="340" Background="#333333" BorderThickness="0" IsReadOnly="True" Foreground="LightGray" FontSize="16" Text="A Windows upgrade is available. Click to upgrade or schedule update." HorizontalContentAlignment="Left" TextWrapping="Wrap" IsHitTestVisible="False"/>
</StackPanel>
</StackPanel>
</Border>
<Grid.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard Name="BeginToastAnimationStoryboard">
<Storyboard Name="ToastAnimationStoryboard" FillBehavior="Stop">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleY)" FillBehavior="Stop">
<SplineDoubleKeyFrame KeyTime="0:0:0" Value="0"/>
<SplineDoubleKeyFrame KeyTime="0:0:0.5" Value="1"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" FillBehavior="Stop">
<SplineDoubleKeyFrame KeyTime="0:0:18" Value="1"/>
<SplineDoubleKeyFrame KeyTime="0:0:20" Value="0"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="FrameworkElement.MouseUp">
<SkipStoryboardToFill BeginStoryboardName="BeginToastAnimationStoryboard"/>
<RemoveStoryboard BeginStoryboardName="BeginToastAnimationStoryboard"/>
</EventTrigger>
</Grid.Triggers>
<Grid.RenderTransform>
<ScaleTransform ScaleY="1" />
</Grid.RenderTransform>
</Grid>
With Code Behind:
using System;
using System.Windows;
using System.Windows.Threading;
public partial class NotificationWindow
{
public NotificationWindow()
{
InitializeComponent();
Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new Action(() =>
{
var workingArea = System.Windows.Forms.Screen.PrimaryScreen.WorkingArea;
var transform = PresentationSource.FromVisual(this).CompositionTarget.TransformFromDevice;
var corner = transform.Transform(new Point(workingArea.Right, workingArea.Bottom));
this.Left = corner.X - this.ActualWidth - 10;
this.Top = corner.Y - this.ActualHeight;
}));
}
}
The first time the notification is shown, I use the Loaded event handler to call the Begin method with isControllable set to True. After that, I use the IsVisibleChanged event handler to start the animation if the Visibility property of the window is set to Visible.
Upvotes: 3
Views: 1794
Reputation: 16106
Change the MouseUp EventTrigger to:
Here is the change in xaml:
<EventTrigger RoutedEvent="FrameworkElement.MouseUp">
<SeekStoryboard BeginStoryboardName="BeginToastAnimationStoryboard" />
<PauseStoryboard BeginStoryboardName="BeginToastAnimationStoryboard" />
</EventTrigger>
Resetting the animation ensures that when the animation is restarted it will start again from the beginning of the storyboard.
Upvotes: 1
Reputation: 4138
I would recommend to create a new instance for each notification. Let me explain why.
For this to work however you must ensure that the window is very light and can be initialized very fast.
Upvotes: 1