我遇到了自定义wpf启动画面实现的问题。 问题是在加载完成并且应该显示MainWindow之后,它有时不会被带到前面,即Activate()调用失败。它可能发生1/10倍。应用程序在Windows7 / 64上运行。
这是implmentation(完整来源sample)
public partial class App : Application
{
private Splash _splash;
private SplashVM _viewModel;
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
// starts splash in separate GUI thread
StartSplash();
// continues on loading main application in main gui thread
LoadMainAppFakeSteps(1000, 3);
// tells splash screen to start shutting down
Stop();
// Creates mainwindow for application
// The problem is that the mainwindow sometimes fails to activate,
// even when user has not touched mouse or keyboard (i.e has not given any other programs or components focus)
MainWindow = new Shell();
MainWindow.Show();
MainWindow.Activate();
}
private void StartSplash()
{
_viewModel = new SplashVM();
var thread = new Thread(SplashThread);
thread.SetApartmentState(ApartmentState.STA);
thread.IsBackground = true;
thread.Start(_viewModel);
}
private void SplashThread(object vm)
{
_splash = new Splash();
_splash.DataContext = vm;
_splash.Show();
System.Windows.Threading.Dispatcher.Run();
_splash = null;
_viewModel = null;
}
private void LoadMainAppFakeSteps(int stepDelayMs, int numSteps)
{
for (int i = 1; i <= numSteps; i++)
{
_viewModel.Text = i.ToString();
Thread.Sleep(stepDelayMs);
}
}
private void Stop()
{
if (_splash == null) throw new InvalidOperationException("Not showing splash screen");
_splash.Dispatcher.BeginInvokeShutdown(DispatcherPriority.Normal);
}
}
我试过了:
MainWindow = new Shell();
MainWindow.Topmost = true;
MainWindow.Show();
MainWindow.Activate();
MainWindow.Topmost = false;
它似乎有效,感谢您的所有建议
答案 0 :(得分:4)
Windows具有一些内置保护功能,可防止Windows从用户当前活动的线程中窃取焦点。这意味着您只能从另一个当前具有焦点的窗口成功Activate()
表单。
例如,如果您的主应用程序窗口显示一个对话框,Windows很高兴让它具有焦点,因为主应用程序只是将焦点放在同一个线程中,因此可以传递它。
在启动画面的情况下,由于多种原因,您的主窗体可能不是聚焦窗口。要么是因为您的启动画面有焦点,要么是因为最终用户开始加载您的应用程序然后开始做其他事情(可能正在使用他们的电子邮件或浏览器),这意味着您的应用程序根本没有焦点。 (您可能会在任务栏中看到窗口闪烁,试图获得焦点?)
在第一种情况下,您希望启动画面激活主窗体然后关闭。 (因为首先关闭启动画面然后尝试激活主窗体)。如果你搞乱了线程,你也可以使用一些win32 pinvoke来使用窗口句柄来调用SetForegroundWindow()
。您可以跨线程传递窗口句柄。这将确保窗口适合您的焦点请求,因为当前关注的表单正在发出请求。
这是一篇关于窃取焦点主题的简洁文章:
Windows 2000和Windows XP让你带来的唯一方法 应用程序的窗口直到前面是它正在运行的线程 是当时前景窗口的线程。所以,你必须这样做 将应用程序的线程附加到前台的线程 窗口然后将您的应用程序窗口移到前面。后 您需要分离您的线程(这一切只有在用户发生时才会发生) 将另一个应用程序的窗口作为前台活动窗口)。 来源: Force Window to Front
有人在此另一篇StackOverflow帖子中将此文章的代码转换为C#:Steal Focus in C# on StackOverflow(看起来PInvoke调用的某些定义缺失,但您可以在google上使用“pinvoke”轻松找到这些,并且名称为你想要的Win32 API。)
答案 1 :(得分:3)
MainWindow = new Shell();
MainWindow.Topmost = true;
MainWindow.Show();
MainWindow.Activate();
MainWindow.Topmost = false;
答案 2 :(得分:1)
除了hkon的回答,你应该在通过TopMost = true窃取焦点之前检查你的启动画面是否是当前的前景窗口。
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
public static bool IsForeground(this Window window)
{
var windowHandle = new WindowInteropHelper(window).Handle;
var foregroundWindow = GetForegroundWindow();
return windowHandle == foregroundWindow;
}