【WPF踩坑】-后台窗口切换到前台

引入

在开发WPF应用时,你可能会需要把后台窗体切换到前台。

就是有A和B两个窗口,用户把B窗口最小化了,但是现在你需要把的B窗口用代码的方式切换到前台,而B窗口放在A窗口后面

过去的做法

可能你会看到很多文章,告诉你直接像下面一样,执行这段代码即可:

window.Activate()

但实际上,这行代码会一点作用也没有。

现在的做法

经过一段时间的检索和排查,我们终于在lindexi大佬的博客中,找到了解决方案。

[WPF 让窗口激活作为前台最上层窗口的方法]

 var interopHelper = new WindowInteropHelper(window);
 var thisWindowThreadId = Win32.User32.GetWindowThreadProcessId(interopHelper.Handle, IntPtr.Zero);
 var currentForegroundWindow = Win32.User32.GetForegroundWindow();
 var currentForegroundWindowThreadId = Win32.User32.GetWindowThreadProcessId(currentForegroundWindow, IntPtr.Zero);

            // [c# - Bring a window to the front in WPF - Stack Overflow](https://stackoverflow.com/questions/257587/bring-a-window-to-the-front-in-wpf )
            // [SetForegroundWindow的正确用法 - 子坞 - 博客园](https://www.cnblogs.com/ziwuge/archive/2012/01/06/2315342.html )
            /*
                 1.得到窗口句柄FindWindow 
                2.切换键盘输入焦点AttachThreadInput 
                3.显示窗口ShowWindow(有些窗口被最小化/隐藏了) 
                4.更改窗口的Zorder,SetWindowPos使之最上,为了不影响后续窗口的Zorder,改完之后,再还原 
                5.最后SetForegroundWindow 
             */
            Win32.User32.AttachThreadInput(currentForegroundWindowThreadId, thisWindowThreadId, true);

            window.Show();
            window.Activate();
            // 去掉和其他线程的输入链接
            Win32.User32.AttachThreadInput(currentForegroundWindowThreadId, thisWindowThreadId, false);

            // 用于踢掉其他的在上层的窗口
            window.Topmost = true;
            window.Topmost = false;

引入Win32 Api之后,确实解决了大部分问题。

存在的问题

当然,大佬也在博客里面说了,会有坑。

这篇文章还能水,那肯定是我踩到坑了。

Topmost问题

首先,会遇到Topmost问题。

代码中因为要使用Topmost,踢掉之前的上层窗口。

但是在第二次取消设置Topmost的时候,Topmost = false有概率不生效。

切换前台失效问题

正如大佬所说,因为可能会有坑,所以不一定100%工作。

而这个问题,在于后台的窗口如果已经最大化,那么后台窗体就不会切换到前台。

我的改进

Topmost问题

解决Topmost问题,需要在激活窗体的时候演示设置Topmost = false,这样能够避免同时设置Topmost 的值而不生效。

Task.Run(async () =>
    {
        await Task.Delay(200);
        KyrioGUI.RunOnUIThread(() =>
           {
                window.Topmost = false;
                //window.WindowState = WindowState.Maximized;
            });
    });

做个异步Lambda,然后await Task.Delay,然后在同步线程中执行取消设置Topmost 即可。

KyrioGUI.RunOnUIThread是我自己实现的切换到UI线程执行的代码,你也可以用SynchronizationContext.Current.Send来代替,最终效果是一样的。

切换前台失效问题

至于为什么,可能需要由大佬再研究了,我的直觉告诉我这部分的坑都来自Win32部分。

不过我没时间深究这些细节了,我给出的解决办法就是:

 //
 //
 AttachThreadInput(currentWindowThreadID, threadID, true);
            
 //window.Show();
 window.Activate();
            
 AttachThreadInput(currentWindowThreadID, threadID, false);
            
 // 用于踢掉其他的在上层的窗口
 window.Topmost = true;
 window.WindowState = WindowState.Normal;

在设置window.Topmost = true之后,紧接着设置window.WindowState = WindowState.Normal,让后台窗口变为普通模式,而不是最大化。

这样代码就可以正常工作了。

至于为什么我注释掉window.Show();,是因为调用这个函数的时候,DEBUG模式下,直接卡住了,连VS都卡住了。

注释掉之后一切恢复正常,代码也不会不工作。

后续

和lindexi大佬反馈了这个坑

posted @   Acoris  阅读(16)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
点击右上角即可分享
微信分享提示