防止WebBrowser控件窃取焦点?

时间:2009-10-13 20:04:38

标签: .net javascript browser focus

有没有办法阻止WebBrowser控件导致其父窗体将自己带到前面?

如果使用InvokeScript方法调用在主父文档中的iframe上调用focus()的JavaScript函数,则会导致窗口直接将其自身带到前面(或至少导致任务栏图标开始闪烁)。有没有办法防止这种情况发生?

更新

我找到了问题的临时答案。

当触发WebBrowser的父Form的Deactive事件时,我从其容器中删除WebBrowser,并在再次激活旧的父窗体时重新添加它。

这有点hacky,但它确实有效。不过,我愿意接受任何更好的建议。

2 个答案:

答案 0 :(得分:8)

编辑: 重写完整的问题,我误解了原始问题

让我们概括一下这个问题:一个你无法控制的控件或组件,可以调用FlashWindow(Win32 API函数)来引起用户的注意。你不希望这样。

通常有两种解决方案:使用API​​挂钩或消息挂钩。由于API挂钩很复杂且涉及到,我将为Message挂钩提供解决方案。

FlashWindow

微软没有用FlashWindow来解释WM_FLASH这么多的话。不幸的是,它不会发送特定的消息(比如FlashWindow或类似的消息),这样可以更容易地捕获和废除此行为。相反,WM_NCACTIVATE做了三件事:

  1. 设置闪烁间隔的系统计时器
  2. 它会为第一个闪存发送WM_NCACTIVATE消息
  3. 当计时器到期时(收到WM_SYSTIMER时)发送WM_SYSTIMER消息
  4. 根据组件调用FlashWindow的方式,这可能是无限期的,直到发生另一次超时,直到它有焦点或只有一次。每个WM_NCACTIVATE消息激活或停用NC区域(标题栏,任务栏上的按钮)。它不会改变输入焦点。

    挑战

    任何防止闪烁的解决方案都涉及到一些问题。主要挑战是:

    1. WndProc事件与PostMessage异步发送,并且不会被表单的WM_NCACTIVATE方法接收(它只处理同步消息)
    2. 当用户点击标题栏或任务栏按钮设置输入焦点时,也会使用WM_SYSTIMER消息,只需取消这些消息就会产生不必要的副作用
    3. FlashWindow将始终闪烁至少一次,无论WM_SYSTIMER发射与否。
    4. 0x0118消息未记录。它具有值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); } ,并在Windows内部用于计时插入符号的闪烁,菜单打开的延迟等。这里用于闪烁之间的时间。

      解决方案

      我在此提出的解决方案是进一步发展的基础。它不是一个完整的解决方案,但在许多情况下它解决了这个问题。将以下内容放在表单代码中:

      WM_NCACTIVATE

      以上代码已经完全阻止了闪烁。您必须添加一些逻辑来更改标题栏,因为完全忽略WM_SYSTIMER意味着标题栏将始终显示为活动状态,即使它不是。

      以下代码为您提供更多控制权。您可以使用它来响应闪烁本身。通常情况下,主窗口不会经常收到FlashWindow个事件,但您必须尝试是否应该例外。似乎对于wParam0xFFF8始终设置为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));
      

      要激活此messagefilter,只需在表单加载事件中的某处添加以下行:

      public const UInt32 WM_SYSTIMER = 0x0118;
      public const UInt32 WM_NCACTIVATE = 0x86;
      

      以下常量应放在班级。它们用于上面的两个代码部分:

      WinProc

      结论

      虽然问题本身是可以解决的,但到目前为止并不容易。有了上面的句柄,你应该走得很远。使用滤镜防止闪烁,但第一次“闪光”仍然发生。使用{{1}}覆盖来防止第一个覆盖,但添加一些逻辑以防止您的应用程序表现得太奇怪(即:始终处于非活动标题栏,或始终处于活动状态)。您已经有一些代码可以与此结合使用来设置一些布尔标记。

答案 1 :(得分:5)

在对象中实现IProtectFocus :: AllowFocusChange

在实现IOleClientSite的同一对象上实现IServiceProvider,然后 使用公开IProtectFocus的对象响应Ser_SProtectFocus的IServiceProvider :: QueryService

这是IE7中的一个新界面,因此旧版本运气不佳。