防止 WebBrowser 控制項竊取焦點?
有沒有辦法阻止 WebBrowser 控制項導致其父窗體將自身置於最前面?
如果您使用 InvokeScript 方法呼叫在主父文件中的 iframe 上呼叫 focus() 的 JavaScript 函式,它將導致視窗直接將自身帶到前面(或至少導致工作列圖示開始閃爍)。有沒有辦法防止這種情況發生?
更新:
我找到了我的問題的臨時答案。
當 WebBrowser 的父窗體的 Deactive 事件被觸發時,我將 WebBrowser 從其容器中移除,並在其舊的父窗體再次啟動時重新添加它。
這有點hacky,但它有效。不過,我願意接受任何更好的建議。
***編輯:***完整的問題重寫,我誤解了原來的問題
讓我們概括一下這個問題:您無法控制的控制項或組件可以呼叫
FlashWindow(Win32 API 函式)來引起使用者的注意。你不想要那個。通常有兩種解決方案:使用 API 掛鉤或消息掛鉤。由於 API 掛鉤複雜且涉及,我將介紹消息掛鉤的解決方案。
快閃視窗
微軟並沒有用太多的話來解釋是什麼
FlashWindow。不幸的是,它不會發送特定的消息(比如說WM_FLASH或類似的),這會使擷取和取消這種行為變得更容易。相反,FlashWindow做三件事:
- 它為閃爍間隔設置系統計時器
- 它發送
WM_NCACTIVATE第一次閃光的消息WM_NCACTIVATE它在計時器到期時發送一條消息(接收時WM_SYSTIMER)根據組件呼叫 FlashWindow 的方式,這可以是無限的,直到發生另一個超時,直到它具有焦點或只有一次。每個 WM_NCACTIVATE 消息啟動或停用 NC 區域(標題欄、工作列上的按鈕)。它不會改變輸入焦點。
挑戰
任何防止閃爍的解決方案都有點涉及。主要挑戰是:
WM_SYSTIMER事件與PostMessage非同步發送,不被Form的方法接收(WndProc只處理同步消息)- 當使用者點擊標題欄或工作列按鈕設置輸入焦點時也會使用這些
WM_NCACTIVATE消息,簡單地取消這些消息會產生不必要的副作用- FlashWindow 將始終至少閃爍一次,無論是否
WM_SYSTIMER觸發。該
WM_SYSTIMER消息未記錄在案。它具有值0x0118並由 Windows 在內部用於計時,例如插入符號的閃爍、菜單打開的延遲等。這裡它用於閃爍之間的時間。解決方案
我在這裡提出的解決方案是進一步發展的基礎。這不是一個完整的解決方案,但它在許多情況下解決了問題。將以下內容放入您的表單程式碼中:
protected override void WndProc(ref Message m) { bool messageHandled = false; if (m.Msg == WM_NCACTIVATE) { // add logic here to determine user action, losing focus etc and set // messageHandled and m.Result only when user action is not the cause // of triggering WM_NCACTIVATE m.Result = IntPtr.Zero; messageHandled = true; } if(!messageHandled) base.WndProc(ref m); }上面的程式碼已經完全阻止了閃爍。您必須添加一些邏輯來更改標題欄,因為完全忽略
WM_NCACTIVATE意味著標題欄將始終處於活動狀態,即使它不是。以下程式碼為您提供了更多控制權。您可以使用它對閃爍本身做出反應。通常,主視窗不會
WM_SYSTIMER如此頻繁地接收事件,但是您必須嘗試是否應該進行例外處理。似乎對於FlashWindow,wParam總是設置為0xFFF8,但請嘗試使用它,因為這在任何地方都沒有記錄。public class MyMessageFilter : IMessageFilter { // an application can have many windows, only filter for one window at the time IntPtr FilteredHwnd = IntPtr.Zero; public MyMessageFilter(IntPtr hwnd) { this.FilteredHwnd = hwnd; } public bool PreFilterMessage(ref Message m) { if (this.FilteredHwnd == m.HWnd && m.Msg == WM_SYSTIMER) return true; // stop handling the message further else return false; // all other msgs: handle them } }要啟動此消息過濾器,只需在表單載入事件中的某處添加以下行:
Application.AddMessageFilter(new MyMessageFilter(this.Handle));以下常量應放在類級別。它們用於上面的兩個程式碼部分:
public const UInt32 WM_SYSTIMER = 0x0118; public const UInt32 WM_NCACTIVATE = 0x86;結論
雖然問題本身是可以解決的,但遠非易事。使用上述搖桿,您應該可以走得很遠。使用過濾器防止閃爍,但第一次“閃爍”仍然發生。也使用
WinProc覆蓋來防止第一個,但添加一些邏輯以防止您的應用程序行為過於奇怪(即:始終處於非活動狀態的標題欄,或始終處於活動狀態)。您已經有了一些程式碼,可以結合這些程式碼來設置一些布爾標誌。