【问题标题】:Bringing mainwindow to front after splash-screen shuts down启动画面关闭后将主窗口置于前面
【发布时间】:2011-11-10 11:17:34
【问题描述】:

我遇到了自定义 wpf 闪屏实现的问题。 问题是在加载完成并且应该显示 MainWindow 之后,它有时没有被带到前面,即 Activate() 调用失败。它可能发生 1/10 次。应用程序在 Windows7/64 上运行。

这是实现(完整源代码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;

它似乎有效,感谢您的所有建议

【问题讨论】:

  • 在 Windows XP 上尝试了大约 30 次,但我从未注意到这个问题。您在哪个操作系统上遇到此问题?
  • 我也无法重现该问题。我正在使用 Windows 7。
  • 为我工作没有问题,也有 Windows7、64 位..
  • +1 我有 exact 相同的问题(Windows 7 64 位)。在调试器下启动应用程序时不会发生这种情况,但在使用 F6 从 VS 中启动应用程序时会发生这种情况,有时也只是在资源管理器中运行 exe 时会发生这种情况

标签: wpf splash-screen


【解决方案1】:

Windows 具有一些内置保护功能,可防止 Windows 窃取用户当前活动线程的焦点。这意味着您只能从当前具有焦点的另一个窗口成功Activate() 一个表单。

例如,如果您的主应用程序窗口显示一个对话框,则窗口很乐意使其具有焦点,因为主应用程序只是在同一个线程中获得焦点,因此可以传递它。

在初始屏幕的情况下,您的主窗体可能不是焦点窗口,原因有几个。要么是因为你的初始屏幕有焦点,要么是因为最终用户开始加载你的应用程序,然后开始做其他事情(可能是玩他们的电子邮件或浏览器),这意味着你的应用程序根本没有焦点。 (您可能会看到任务栏中的窗口闪烁试图获得焦点?)

在第一种情况下,您希望启动屏幕激活主窗体然后关闭。 (与先关闭启动屏幕然后尝试激活主窗体相反)。如果你弄乱了线程,你也可以使用一些 win32 pinvoke 来调用 SetForegroundWindow() 与窗口句柄。您可以跨线程传递窗口句柄。这将确保 windows 能够满足您的焦点请求,因为当前焦点窗体正在发出请求。

这是一篇关于窃取焦点主题的简洁文章:

Windows 2000 和 Windows XP 让您将自己的 应用程序的最前面的窗口是它正在运行的线程 是当时前台窗口的线程。所以,你必须 将应用程序的线程附加到前台线程 窗口,然后将应用程序的窗口置于最前面。后 你需要分离你的线程(这一切只发生在用户 有另一个应用程序的窗口作为前台活动窗口)。 来源: Force Window to Front

有人在另一篇 StackOverflow 帖子中将本文的代码转换为 C#:Steal Focus in C# on StackOverflow(看起来 PInvoke 调用的一些定义丢失了,但您可以通过“pinvoke”和 Win32 API 的名称在 google 上轻松找到这些你想要的。)

【讨论】:

    【解决方案2】:
    MainWindow = new Shell();
    MainWindow.Topmost = true;
    MainWindow.Show();
    MainWindow.Activate();
    MainWindow.Topmost = false;
    

    【讨论】:

    • 这似乎确实可以解决它,可能是通过强制窃取焦点。甚至不需要激活调用。
    【解决方案3】:

    除了 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;
        }
    

    【讨论】:

    • 是的,这是礼貌的做法 =)
    猜你喜欢
    • 1970-01-01
    • 2015-09-25
    • 2019-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-02
    • 2011-11-04
    相关资源
    最近更新 更多