Dot-Net

防止 WebBrowser 控制項竊取焦點?

  • April 8, 2013

有沒有辦法阻止 WebBrowser 控制項導致其父窗體將自身置於最前面?

如果您使用 InvokeScript 方法呼叫在主父文件中的 iframe 上呼叫 focus() 的 JavaScript 函式,它將導致視窗直接將自身帶到前面(或至少導致工作列圖示開始閃爍)。有沒有辦法防止這種情況發生?

更新:

我找到了我的問題的臨時答案。

當 WebBrowser 的父窗體的 Deactive 事件被觸發時,我將 WebBrowser 從其容器中移除,並在其舊的父窗體再次啟動時重新添加它。

這有點hacky,但它有效。不過,我願意接受任何更好的建議。

***編輯:***完整的問題重寫,我誤解了原來的問題

讓我們概括一下這個問題:您無法控制的控制項或組件可以呼叫FlashWindow(Win32 API 函式)來引起使用者的注意。你不想要那個。

通常有兩種解決方案:使用 API 掛鉤或消息掛鉤。由於 API 掛鉤複雜且涉及,我將介紹消息掛鉤的解決方案。

快閃視窗

微軟並沒有用太多的話來解釋是什麼FlashWindow。不幸的是,它不會發送特定的消息(比如說WM_FLASH或類似的),這會使擷取和取消這種行為變得更容易。相反,FlashWindow做三件事:

  1. 它為閃爍間隔設置系統計時器
  2. 它發送WM_NCACTIVATE第一次閃光的消息
  3. WM_NCACTIVATE它在計時器到期時發送一條消息(接收時WM_SYSTIMER

根據組件呼叫 FlashWindow 的方式,這可以是無限的,直到發生另一個超時,直到它具有焦點或只有一次。每個 WM_NCACTIVATE 消息啟動或停用 NC 區域(標題欄、工作列上的按鈕)。它不會改變輸入焦點。

挑戰

任何防止閃爍的解決方案都有點涉及。主要挑戰是:

  1. WM_SYSTIMER事件與PostMessage非同步發送,不被Form的方法接收(WndProc只處理同步消息)
  2. 當使用者點擊標題欄或工作列按鈕設置輸入焦點時也會使用這些WM_NCACTIVATE消息,簡單地取消這些消息會產生不必要的副作用
  3. 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覆蓋來防止第一個,但添加一些邏輯以防止您的應用程序行為過於奇怪(即:始終處於非活動狀態的標題欄,或始終處於活動狀態)。您已經有了一些程式碼,可以結合這些程式碼來設置一些布爾標誌。

引用自:https://stackoverflow.com/questions/1562619