following situation: I got a splashscreen which has to fade in over 2 seconds (from transparent to opaque). While the window nicely fades in, the program should wait until it is fully visible (the Storyboard finished) and then it should continue doing it's stuff. While the window is fading in, the user should already see the animations of the control (so blocking the UI thread is not an option, of course). And with animations I mean the loading circle which spins happily.
After I found a nice variant of how to fade in and out a window with WPF's storyboards, I tried to accomplish this by using the EventWaitHandle. As my intialization routine already runs asynchronous I was able to use it. This blocked the worker thread and stopped my application doing the initialization stuff before the Splashscreen was fully visible. But somehow this has been broken after a while and it doesn't seem to be the best solution.
Here's how I'm doing it, currently:
public async Task Appear(double time)
{
Core.IDE.GetGUICore().GetUIDispatcher().Invoke(() =>
{
this.Opacity = 0;
this.Show();
_fadeInStoryboard = new Storyboard();
_fadeInStoryboard.Completed += this.FadeInAnimation;
DoubleAnimation fadeInAnimation = new DoubleAnimation(0.0, 1.0, new Duration(TimeSpan.FromSeconds(time)));
Storyboard.SetTarget(fadeInAnimation, this);
Storyboard.SetTargetProperty(fadeInAnimation, new PropertyPath(OpacityProperty));
_fadeInStoryboard.Children.Add(fadeInAnimation);
});
_currentHandle = new EventWaitHandle(false, EventResetMode.AutoReset);
Core.IDE.GetGUICore()
.GetUIDispatcher()
.InvokeAsync(this._fadeInStoryboard.Begin, DispatcherPriority.Render);
_currentHandle.WaitOne();
}
/// <summary>
/// Hides the SplashScreen with a fade-out animation.
/// </summary>
/// <param name="time">The fade-out time in seconds.</param>
public void Disappear(double time)
{
Core.IDE.GetGUICore().GetUIDispatcher().Invoke(() =>
{
_fadeOutStoryboard = new Storyboard();
_fadeOutStoryboard.Completed += this.FadeOutAnimation;
DoubleAnimation fadeOutAnimation = new DoubleAnimation(1.0, 0.0,
new Duration(TimeSpan.FromSeconds(time)));
Storyboard.SetTarget(fadeOutAnimation, this);
Storyboard.SetTargetProperty(fadeOutAnimation, new PropertyPath(OpacityProperty));
_fadeOutStoryboard.Children.Add(fadeOutAnimation);
});
Core.IDE.GetGUICore()
.GetUIDispatcher()
.BeginInvoke(new Action(_fadeOutStoryboard.Begin), DispatcherPriority.Render, null);
}
private void FadeInAnimation(object sender, EventArgs e)
{
_currentHandle.Set();
}
private void FadeOutAnimation(object sender, EventArgs e)
{
this.Hide();
}
Is this the right approach? Any better solutions? Any ideas why it is broken? By the way, with broken I mean that the application continues doing its initialization stuff whilst the window is fading in, which ends in an animation which runs until it's probably at 30% visibility and then fading out because the main window already showed up.
Thanks in advance
If you declare your Storyboard
in XAML, it really will simplify things for you. Try using this Trigger
in your splash screen control:
<Grid>
<Grid.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard Duration="0:0:5" Completed="Storyboard_Completed">
<DoubleAnimation From="0.0" To="1.0"
Storyboard.TargetProperty="Opacity" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Grid.Triggers>
<!--Put your UI elements here-->
</Grid>
You can load the rest of your app from the Storyboard_Completed
handler, or if this is in a separate Window
, then you can raise some event or delegate
from there, which you should handle in the MainWindow
.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.