有没有一种方法可以阻止WebBrowser控件导致其父窗体显示在最前面?

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

更新:

我已经找到问题的临时答案。

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

这有点hacky,但是可以用。不过,我愿意接受任何更好的建议。

最佳答案

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

我们来概括一下这个问题:您没有控件的控件或组件可以调用FlashWindow(Win32 API函数)来引起用户的注意。你不要那样

通常有两种解决方案:使用API​​挂钩或消息挂钩。由于API挂钩非常复杂且涉及很多,因此我将介绍消息挂钩的解决方案。

Flash窗口

微软没有用太多的词来解释FlashWindow是做什么的。不幸的是,它没有发送特定的消息(例如WM_FLASH或类似消息),这会使捕获和废除此行为变得更加容易。相反,FlashWindow做三件事:

  • 它为闪烁间隔
  • 设置系统计时器
  • 它为第一个Flash
  • 发送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意味着标题栏将一直保持 Activity 状态,即使没有激活也是如此。

    以下代码为您提供了更多控制。您可以使用它来对闪烁本身使用react。通常,主窗口不会如此频繁地接收WM_SYSTIMER事件,但是您必须尝试是否应该设置异常(exception)。似乎对于FlashWindowwParam始终设置为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替代来阻止第一个,但要添加一些逻辑以防止您的应用程序表现得异常(即:标题栏始终处于非 Activity 状态,或始终处于 Activity 状态)。您已经有一些代码,可以将其与此结合以设置一些 bool(boolean) 标志。

    07-24 18:55