C# - Capturing CTRL-Mouse wheel in WebBrowser control
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:
- 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 theWebBrowser
control when it does not appear to have focus even though the parent form has focus. - CTRL-MouseWheel never fires the
MouseWheel
event when the mouse cursor is hovering over theWebBrowser
control andWebBrowser
has focus, and there seems to be no effect. - Using the mouse wheel without CTRL scrolls the window contents of
WebBrowser
but does not fire theMouseWheel
event until the vertical scroll bar has fully reached either the top or the bottom. - Intercepting the
Message
forWM_MOUSEWHEEL
by overridingWndProc
andDefWndProc
both for a sample class inherited fromWebBrowser
and for the parent form applies only for the above conditions (withwParam
properly denotingMK_CONTROL
). - 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开发者_JAVA技巧 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!
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;
[...]
}
Perhaps using SetWindowsHookEx to look for these events may work for you. This is what I've used to get scroll wheel events on top of the ActiveX MapPoint control.
Be aware there are some quirks with this on Windows 7 that may require some tinkering. For more details do a search for SetWindowsHookEx on Windows 7 on the MSDN forums.
To solve this problem you have to listen for and handle these messages:
- OLECMDID_GETZOOMRANGE
- OLECMDID_OPTICAL_GETZOOMRANGE
- OLECMDID_OPTICAL_ZOOM
- OLECMDID_ZOOM
They're dispatched by Internet Explorer. See the remarks on MSDN.
This is the code I used to disable ctrl+shift:
You need to change the behavior of WndProc in the deepest control "Internet Explorer_Server",
Do this after your web browser is ready:
IntPtr wbHandle = Win32.FindWindowEx(this.wbMain.Handle, IntPtr.Zero, "Shell Embedding", String.Empty);
wbHandle = Win32.FindWindowEx(wbHandle, IntPtr.Zero, "Shell DocObject View", String.Empty);
wbHandle = Win32.FindWindowEx(wbHandle, IntPtr.Zero, "Internet Explorer_Server", String.Empty);
WbInternal wb = new WbInternal(wbHandle);
class WbInternal : NativeWindow
{
public WbInternal(IntPtr handle)
{
this.AssignHandle(handle);
}
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_MOUSEWHEEL)
{
if (((int)m.WParam & 0x00FF) == MK_SHIFT)
{
return;
}
}
base.WndProc(ref m);
}
}
You can find more message about WM_MOUSEWHEEL from MSDN.
I find this from MSDN. But I forgot the link, Once find, will append it here.
I couldn't get any of these to work reliably so after some (frustrating) experimentation, I came up with a derivative of the answer posted by TCC. My webbrowser control is hosted in a usercontrol. The main differences are I use a class-level variable for the HTMLDocumentEvents2_Event so I can unsubscribe successfully, and I set the mshtml.IHTMLEventObj pEvtObj.Returnvalue to true.. seems to work well now..
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_wbData = (WebBrowser)FindElement("DataBrowser");
_horzScroll = (ScrollBar)FindElement("HorizontalScroll");
_vertScroll = (ScrollBar)FindElement("VerticalScroll");
_wbData.LoadCompleted += new System.Windows.Navigation.LoadCompletedEventHandler(OnLoadCompleted);
_horzScroll.Scroll += new ScrollEventHandler(OnHorizontalScroll);
_vertScroll.Scroll += new ScrollEventHandler(OnVerticalScroll);
LoadDefault();
EnableSoundEffects(SoundEffects);
}
private void OnHorizontalScroll(object sender, ScrollEventArgs e)
{
// _wbData.Handle
mshtml.HTMLDocument htmlDoc = _wbData.Document as mshtml.HTMLDocument;
_horzPosition = (int)e.NewValue;
if (htmlDoc != null && htmlDoc.body != null)
htmlDoc.parentWindow.scroll(_horzPosition, _vertPosition);
}
private void OnVerticalScroll(object sender, ScrollEventArgs e)
{
mshtml.HTMLDocument htmlDoc = _wbData.Document as mshtml.HTMLDocument;
_vertPosition = (int)e.NewValue;
if (htmlDoc != null && htmlDoc.body != null)
htmlDoc.parentWindow.scroll(_horzPosition, _vertPosition);
}
private void OnLoadCompleted(object sender, System.Windows.Navigation.NavigationEventArgs e)
{
mshtml.HTMLDocument htmlDoc = _wbData.Document as mshtml.HTMLDocument;
if (htmlDoc != null && htmlDoc.body != null)
{
mshtml.IHTMLElement2 body = (mshtml.IHTMLElement2)htmlDoc.body;
_horzScroll.Visibility = Visibility.Collapsed;
if (body.scrollHeight > _wbData.ActualHeight)
_vertScroll.Visibility = Visibility.Visible;
else
_vertScroll.Visibility = Visibility.Collapsed;
_vertScroll.ViewportSize = _wbData.ActualHeight;
_vertScroll.Maximum = body.scrollHeight - (_wbData.ActualHeight - 8);
_eventHelper = (HTMLDocumentEvents2_Event)_wbData.Document;
_eventHelper.onmousewheel -= OnMouseWheel;
_eventHelper.onmousewheel += new HTMLDocumentEvents2_onmousewheelEventHandler(OnMouseWheel);
}
}
private bool OnMouseWheel(mshtml.IHTMLEventObj pEvtObj)
{
mshtml.HTMLDocument htmlDoc = _wbData.Document as mshtml.HTMLDocument;
var wheelEventObj = (mshtml.IHTMLEventObj4)pEvtObj;
var delta = wheelEventObj.wheelDelta;
if (htmlDoc != null && htmlDoc.body != null && wheelEventObj != null)
{
_vertPosition += (int)wheelEventObj.wheelDelta;
htmlDoc.parentWindow.scroll(_horzPosition, _vertPosition);
}
pEvtObj.returnValue = true;
return true;
}
精彩评论