引入
在开发WPF应用时,你可能会需要把后台窗体切换到前台。
就是有A和B两个窗口,用户把B窗口最小化了,但是现在你需要把的B窗口用代码的方式切换到前台,而B窗口放在A窗口后面
过去的做法
可能你会看到很多文章,告诉你直接像下面一样,执行这段代码即可:
window.Activate()
但实际上,这行代码会一点作用也没有。
现在的做法
经过一段时间的检索和排查,我们终于在lindexi
大佬的博客中,找到了解决方案。
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大佬反馈了这个坑
标签:WindowState,窗口,Topmost,Win32,window,前台,后台,WPF From: https://www.cnblogs.com/luoyisi/p/18649972