防止WebBrowser控件窃取焦点?

ros*_*ead 12 .net javascript browser focus

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

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

更新:

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

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

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

Abe*_*bel 8

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

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

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

FlashWindow

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

  1. 它为闪烁间隔设置系统计时器
  2. 它发送WM_NCACTIVATE第一个闪存的消息
  3. WM_NCACTIVATE在定时器到期时发送消息(在接收时WM_SYSTIMER)

根据组件调用FlashWindow的方式,这可能是无限期的,直到发生另一次超时,直到它有焦点或只有一次.每个WM_NCACTIVATE消息激活或停用NC区域(标题栏,任务栏上的按钮).它不会改变输入焦点.

挑战

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

  1. 所述WM_SYSTIMER事件与PostMessage的异步发送的,而不是由接收到的WndProc表单的方法(它仅处理同步消息)
  2. WM_NCACTIVATE消息也用于当标题栏或任务栏按钮用户点击设置输入焦点,简单地消除这些消息将具有不希望的副作用
  3. 无论是否WM_SYSTIMER发射,FlashWindow将始终闪烁至少一次.

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);
}
Run Code Online (Sandbox Code Playgroud)

上面的代码已经完全阻止了闪烁.您将不得不添加一些逻辑来更改标题栏,因为完全忽略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
    }
}
Run Code Online (Sandbox Code Playgroud)

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

Application.AddMessageFilter(new MyMessageFilter(this.Handle));
Run Code Online (Sandbox Code Playgroud)

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

public const UInt32 WM_SYSTIMER = 0x0118;
public const UInt32 WM_NCACTIVATE = 0x86;
Run Code Online (Sandbox Code Playgroud)

结论

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


She*_* 蒋晟 5

在对象中实现IProtectFocus :: AllowFocusChange

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

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