在 WPF 中,如果想要使用代碼控制,讓某個視窗作為當前用戶的輸入的邏輯焦點的視窗,也就是在當前用戶活動的視窗的最上層視窗,預設使用 Activate 方法,通過這個方法在大部分設備都可以做到激活視窗 ...
在 WPF 中,如果想要使用代碼控制,讓某個視窗作為當前用戶的輸入的邏輯焦點的視窗,也就是在當前用戶活動的視窗的最上層視窗,預設使用 Activate 方法,通過這個方法在大部分設備都可以做到激活視窗
但是在一些特殊的設備上,使用下麵代碼調起視窗只是在任務欄閃爍圖標,而沒有讓視窗放在最上層
window.Show();
window.Activate();
在大部分設備上,通過 Show 和 Activate 組合可以讓視窗作為當前用戶活動的,即使視窗之前是最小化或隱藏,都可以通過 Show 的方法顯示
但是某些設備視窗被蓋在其他的視窗的下麵,此時的視窗的 window.IsActive 還是 true 但是調用 Activate 不會讓視窗放在上層
我在網上看到好多小伙伴調用了 SetForegroundWindow 方法,其實現在 WPF 是開源的,可以看到 Window 的 Activate 方法是這樣寫
public bool Activate()
{
// this call ends up throwing an exception if Activate
// is not allowed
VerifyApiSupported();
VerifyContextAndObjectState();
VerifyHwndCreateShowState();
// Adding check for IsCompositionTargetInvalid
if (IsSourceWindowNull || IsCompositionTargetInvalid)
{
return false;
}
return UnsafeNativeMethods.SetForegroundWindow(new HandleRef(null, CriticalHandle));
}
源代碼請看 github
也就是調用 SetForegroundWindow 和調用 Activate 方法是差不多的,如果調用 Activate 應該調用 SetForegroundWindow 也差不多
通過大佬的 SetForegroundWindow的正確用法 - 子塢 - 博客園 可以瞭解到,需要按照以下步驟
1.得到視窗句柄FindWindow
2.切換鍵盤輸入焦點AttachThreadInput
3.顯示視窗ShowWindow(有些視窗被最小化/隱藏了)
4.更改視窗的Zorder,SetWindowPos使之最上,為了不影響後續視窗的Zorder,改完之後,再還原
5.最後SetForegroundWindow
在 WPF 中對應的更改視窗的順序使用的是 Topmost 屬性,同時設置順序需要做一定小的更改
在 WPF 中通過 c# - Bring a window to the front in WPF - Stack Overflow 可以瞭解到如何用 AttachThreadInput 方法
整個代碼請看下麵,具體的 win32 方法我就沒有寫出來了,請小伙伴自己添加
private static void SetWindowToForegroundWithAttachThreadInput(Window window)
{
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;
我測試了幾個原本沒有讓視窗放在上層的設備,使用上面的代碼可以設置,但是我不瞭解設置上面代碼可能的坑是什麼
附帶 walterlv 的測試工具,可以用來拿到當前的 GetForegroundWindow 是哪個
另外少君小伙伴寫了一個有趣的庫,裡面封裝了很多 win32 的方法,請看 kkwpsv lsjutil