1. 程式人生 > WINDOWS開發 >WPF 讓視窗啟用作為前臺最上層視窗的方法

WPF 讓視窗啟用作為前臺最上層視窗的方法

原文:WPF 讓視窗啟用作為前臺最上層視窗的方法

在 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.更改視窗的ZorderSetWindowPos使之最上,為了不影響後續視窗的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,false);

            // 用於踢掉其他的在上層的視窗
            window.Topmost = true;
            window.Topmost = false;

我測試了幾個原本沒有讓視窗放在上層的裝置,使用上面的程式碼可以設定,但是我不瞭解設定上面程式碼可能的坑是什麼

附帶 walterlv 的測試工具,可以用來拿到當前的 GetForegroundWindow 是哪個

walterlv 的工具

另外少君小夥伴寫了一個有趣的庫,裡面封裝了很多 win32 的方法,請看 kkwpsv lsjutil