最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

.net - Prevent WebBrowser control from stealing focus? - Stack Overflow

programmeradmin9浏览0评论

Is there a way to stop the WebBrowser control from causing its parent form to bring itself to the front?

If you use the InvokeScript method to invoke a JavaScript function that calls focus() on an iframe within the main parent document, it will cause the window to bring itself directly to the front(or atleast cause the taskbar icon to start flashing). Is there a way to prevent this from happening?

Update:

I've found a temporary answer to my problem.

When the WebBrowser's parent Form's Deactive event is fired, I remove the WebBrowser from its container, and re-add it when its old parent form is activated again.

It's kind of hacky, but it works. I'm open to any better suggestions, though.

Is there a way to stop the WebBrowser control from causing its parent form to bring itself to the front?

If you use the InvokeScript method to invoke a JavaScript function that calls focus() on an iframe within the main parent document, it will cause the window to bring itself directly to the front(or atleast cause the taskbar icon to start flashing). Is there a way to prevent this from happening?

Update:

I've found a temporary answer to my problem.

When the WebBrowser's parent Form's Deactive event is fired, I remove the WebBrowser from its container, and re-add it when its old parent form is activated again.

It's kind of hacky, but it works. I'm open to any better suggestions, though.

Share Improve this question edited Oct 13, 2009 at 21:37 rossisdead asked Oct 13, 2009 at 20:04 rossisdeadrossisdead 2,1081 gold badge19 silver badges30 bronze badges 3
  • It is probably enough to make it invisible instead of full removal. It is certainly a way out here. Sorry if I didn't understand before that it was about a form in .net, instead of a javascript/html problem – Abel Commented Oct 14, 2009 at 22:13
  • @Abel: It's mostly a javascript/browser problem causing a WinForms problem. I did try just making the browser control invisible first, but it didn't solve it until I flat out removed the control from the form. – rossisdead Commented Oct 15, 2009 at 3:59
  • I completely rewrote the answer. It is not an easy task you're up to, but take my answer, tweak it to your needs, and you can be all set – Abel Commented Oct 15, 2009 at 11:35
Add a comment  | 

2 Answers 2

Reset to default 8

EDIT: complete question rewritten, I misunderstood original question

Let's generalize the problem: a control or component that you don't have control about, can call FlashWindow (the Win32 API function) to get attention from the user. You don't want that.

There are generally two solutions for this: use API hooking or Message hooking. Since API hooking is complex and involved, I'll present a solution for Message hooking.

FlashWindow

Microsoft doesn't explain in so many words what FlashWindow does. Unfortunately, it doesn't send a specific message (say WM_FLASH or similar), which would've made it easier to capture and annul this behavior. Instead, FlashWindow does three things:

  1. It sets a system timer for the flashing intervals
  2. It sends a WM_NCACTIVATE message for the first flash
  3. It sends a WM_NCACTIVATE message when the timer expires (on receiving WM_SYSTIMER)

Depending on how the component calls FlashWindow, this can be indefinite, until another timeout occurs, until it has focus or just once. Each WM_NCACTIVATE message activates or deactivates the NC-zone (caption bar, button on taskbar). It doesn't change the input the focus.

Challenge

Any solution for preventing the flashing is a bit involved. The main challenges are:

  1. the WM_SYSTIMER event is sent asynchronously with PostMessage and is not received by the WndProc method of the Form (it only processes synchronous messages)
  2. the WM_NCACTIVATE messages are also used when the user clicks on the title bar or taskbar button to set input focus, simply canceling these messages will have unwanted side effects
  3. FlashWindow will always flash at least once, regardless of the WM_SYSTIMER firing or not.

The WM_SYSTIMER message is undocumented. It has the value 0x0118 and is used internally by Windows to time such things as the blinking of the caret, the delay in a menu opening etc. Here it is used for the time between the flashes.

Solution

The solution I present here is a basis for further development. It is not a complete solution, but it solves the issue in many cases. Place the following in your form code:

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);
}

The above code already prevents flashing completely. You'll have to add some logic to change the title bar, because totally ignoring WM_NCACTIVATE means that the title bar will look active all the time, even when it isn't.

The following code gives you more control. You can use it to react to the flashing itself. Normally, a main window does not receive WM_SYSTIMER events so often, but you'll have to experiment whether you should make exceptions. It seems that for FlashWindow, the wParam is always set to 0xFFF8, but do experiment with it, as this is not documented anywhere.

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
    }
}

To activate this messagefilter, simply add the following line somewhere in your form load event:

Application.AddMessageFilter(new MyMessageFilter(this.Handle));

The following constants should be placed at class level. They are used in both code sections above:

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

Conclusion

Though the problem itself is solvable, it is by far not easy. With the above handles, you should get quite far. Use the filter to prevent the flashing, but then the first "flash" still happens. Use the WinProc override to prevent the first one too, but add some logic to prevent your application from behaving too oddly (i.e.: always inactive title bar, or always active). You already have some code that you can combine with this to set some boolean flags.

Implement IProtectFocus::AllowFocusChange in an object

Implement IServiceProvider on the same object that implements IOleClientSite, then respond to a IServiceProvider::QueryService for SID_SProtectFocus with an object that exposes IProtectFocus

This is a new interface in IE7, so old versions are out of luck.

发布评论

评论列表(0)

  1. 暂无评论