I have an action I need to perform around 3 seconds after my app starts. I've implemented it as follows:
internal static class Entry
{
private static SplashScreen splashScreen;
[STAThread]
internal static void Main()
{
ShowSplashScreen();
StartApp();
}
private static void ShowSplashScreen()
{
splashScreen = new SplashScreen("Splash.png");
splashScreen.Show(false, true);
}
private static void StartApp()
{
var app = new App();
//this, in particular, is ugly and more difficult to comprehend than I'd like
var dispatcherTimer = new DispatcherTimer();
dispatcherTimer.Interval = TimeSpan.FromSeconds(3)开发者_StackOverflow社区;
dispatcherTimer.Tick += delegate
{
CloseSplashScreen();
dispatcherTimer.Stop();
};
dispatcherTimer.Start();
app.Run();
}
private static void CloseSplashScreen()
{
splashScreen.Close(TimeSpan.FromSeconds(1));
}
}
I find the StartApp()
code rather ugly but have not been able to concoct a neater alternative. Is there a common idiom I'm missing here?
PS. Yes, I'm aware SplashScreen
has an auto-close option. I'm not wanting to use that mainly because it begins closing as soon as the app has loaded, which I don't want to do.
Here is something similar you might be interested in:
How do we do idle time processing in WPF application?
It's not exactly what you are looking for, because it will close your window as soon as your app goes idle, but you might consider to start your delay after your app went idle. You might find that link helpful than.
Do you not have a specific state when your application is done starting? Normally you want your SplashScreen to close when your application is ready to handle user input, instead of an arbitrary 3 secs. So I would suggest to close your SplashScreen then.
This is about the best I could come up with:
internal static class Entry
{
private static SplashScreen splashScreen;
private static App app;
[STAThread]
internal static void Main()
{
ShowSplashScreen();
CreateApp();
PumpDispatcherUntilPriority(DispatcherPriority.Loaded);
PumpDispatcherFor(TimeSpan.FromSeconds(2));
CloseSplashScreen();
PumpDispatcherUntilAppExit();
}
private static void ShowSplashScreen()
{
splashScreen = new SplashScreen("Splash.png");
splashScreen.Show(false, true);
}
private static void CloseSplashScreen()
{
splashScreen.Close(TimeSpan.FromSeconds(0.5));
}
private static void CreateApp()
{
app = new App();
}
private static void PumpDispatcherUntilPriority(DispatcherPriority dispatcherPriority)
{
var dispatcherFrame = new DispatcherFrame();
Dispatcher.CurrentDispatcher.BeginInvoke((ThreadStart)(() => dispatcherFrame.Continue = false), dispatcherPriority);
Dispatcher.PushFrame(dispatcherFrame);
}
private static void PumpDispatcherFor(TimeSpan timeSpan)
{
var dispatcherFrame = new DispatcherFrame();
using (var timer = new Timer(o => dispatcherFrame.Continue = false, null, (long)timeSpan.TotalMilliseconds, Timeout.Infinite))
{
Dispatcher.PushFrame(dispatcherFrame);
}
}
private static void PumpDispatcherUntilAppExit()
{
var dispatcherFrame = new DispatcherFrame();
app.Exit += delegate
{
dispatcherFrame.Continue = false;
};
Dispatcher.PushFrame(dispatcherFrame);
}
}
I toyed with extension methods for Dispatcher
, but ultimately found them less intuitive. That's because PushFrame()
is static
, so any extension methods don't actually execute against the Dispatcher
they're invoked against. YMMV.
Note that you could also call app.Run()
instead of PumpDispatcherUntilAppExit()
, but I just did that for consistency.
Does not really matter if it is ugly, you can just refactor it into a method which takes an Action
as parameter for example and that won't be much of a problem.
As by ugly you probably meant that it looks like bad code i would suggest the use of a normal thread (with Thread.Sleep
before your action) which uses Dispatcher.Invoke
instead. I for one am not aware of any best practice regarding this though. This can also be nicely refactored into a simple method taking an Action
.
If you want a non-blocking wait there is a question to be found about that as well.
精彩评论