C# – Capturing CTRL-Mouse wheel in WebBrowser control

browsercctrlformsmousewheel

I'm developing a Windows Forms application in C# with an embedded WebBrowser control to "dummy-proof" (i.e. disable context menus, back button, free navigation, etc.) access to a third-party web application.

Right now I'm trying to add the Zoom feature to my custom browser. I have the keyboard combos working for it (CTRL + and CTRL – make the correct OLE calls to the underlying ActiveX WebBrowser object), but among the other frustrating things about WebBrowser I've had to deal with, I can't seem to figure out how to capture CTRL-Mouse wheel to simulate the Zoom function like IE does. I've looked everywhere to find a solution to this but to no avail.

To try to figure it out, I created an empty form with just the WebBrowser control in it, and found the following:

  1. CTRL-MouseWheel always fires the MouseWheel event when the parent form has focus and the mouse cursor is hovering over the top of the window (e.g. over the title of the application), or when the mouse cursor is hovering over the WebBrowser control when it does not appear to have focus even though the parent form has focus.
  2. CTRL-MouseWheel never fires the MouseWheel event when the mouse cursor is hovering over the WebBrowser control and WebBrowser has focus, and there seems to be no effect.
  3. Using the mouse wheel without CTRL scrolls the window contents of WebBrowser but does not fire the MouseWheel event until the vertical scroll bar has fully reached either the top or the bottom.
  4. Intercepting the Message for WM_MOUSEWHEEL by overriding WndProc and DefWndProc both for a sample class inherited from WebBrowser and for the parent form applies only for the above conditions (with wParam properly denoting MK_CONTROL).
  5. The PreviewKeyDown event fires when CTRL is pressed, as expected, but still does nothing in unison with the mouse wheel.

So I guess the Message is being processed below the parent form and the managed control level and does not bubble up to where I can intercept or even handle it. Is there a way to do this, or some other way to simulate zooming in and out using CTRL-MouseWheel?

Thanks for reading!

Best Answer

First cast the WebBrowser.Document.DomDocument to the right interface in the mshtml namespace, like mshtml.HTMLDocumentEvents2_Event, then you can handle (and cancel) mousewheel events. I'm not sure, but I think you need to wire up the event handler anytime the document is changed, so I do it on the WebBrowser.DocumentCompleted event. I'm also not sure if you need to release any COM objects.

This was frustrating enough that I got it to work and stopped caring...

Here is at least one document explaining the basics: How to handle document events in a Visual C# .NET application

For your specific case, just conditionally squash the onmousewheel event, based on whether or not the CTRL key is pressed.

private void webBrowser_DocumentCompleted(object sender,
                                         WebBrowserDocumentCompletedEventArgs e)
{
    if (webBrowser.Url.ToString() == "about:blank")
        return;
    var docEvents = (mshtml.HTMLDocumentEvents2_Event)webBrowser.Document.DomDocument;
    docEvents.onmousewheel -= docEvents_onmousewheel; //may not be necessary?
    docEvents.onmousewheel += docEvents_onmousewheel;
}

bool docEvents_onmousewheel(mshtml.IHTMLEventObj pEvtObj)
{
    if (pEvtObj.ctrlKey)
    {
        pEvtObj.cancelBubble = true; //not sure what this does really
        pEvtObj.returnValue = false;  //this cancels the event
        return false; //not sure what this does really
    }
    else
        return true; //again not sure what this does
} 

Now if you need to know the Wheel Delta (scroll amount), you'll want to cast the events object to yet another interface.

bool docEvents_onmousewheel(mshtml.IHTMLEventObj pEvtObj)
{
    var wheelEventObj = (mshtml.IHTMLEventObj4)pEvtObj;
    var delta = wheelEventObj.wheelDelta;
    [...]
}
Related Topic