C# - 在 WebBrowser 控件中捕获 CTRL-鼠标滚轮

发布于 2024-09-28 08:15:28 字数 1243 浏览 3 评论 0原文

我正在用 C# 开发一个 Windows 窗体应用程序,其中嵌入了 WebBrowser 控件,以“防伪”(即禁用上下文菜单、后退按钮、自由导航等)访问第三方 Web应用。

现在我正在尝试将缩放功能添加到我的自定义浏览器中。我有键盘组合(CTRL + 和 CTRL - 对底层 ActiveX WebBrowser 对象进行正确的 OLE 调用),但在我必须处理的关于 WebBrowser 的其他令人沮丧的事情中,我似乎不知道如何像 IE 那样捕获 CTRL-鼠标滚轮来模拟缩放功能。我到处寻找解决方案,但没有成功。

为了尝试弄清楚这一点,我创建了一个空表单,其中仅包含 WebBrowser 控件,并发现以下内容:

  1. 当父窗体具有焦点并且鼠标光标悬停在窗口顶部(例如,悬停在应用程序的标题上)时,或者当鼠标光标悬停时,CTRL-MouseWheel 始终会触发 MouseWheel 事件当 WebBrowser 控件看起来没有焦点时,即使父窗体具有焦点,也会覆盖该控件。
  2. 当鼠标光标悬停在 WebBrowser 控件上并且 WebBrowser 具有焦点时,CTRL-MouseWheel 永远不会触发 MouseWheel 事件,并且似乎没有影响。
  3. 使用不按 CTRL 的鼠标滚轮可滚动 WebBrowser 的窗口内容,但不会触发 MouseWheel 事件,直到垂直滚动条完全到达顶部或底部。
  4. 通过重写继承自 WebBrowser< 的示例类的 WndProcDefWndProc 来拦截 WM_MOUSEWHEELMessage /code> 和父表单仅适用于上述条件(使用 wParam 正确表示 MK_CONTROL)。
  5. 正如预期的那样,当按下 CTRL 时,PreviewKeyDown 事件会触发,但仍然不会与鼠标滚轮同步执行任何操作。

所以我猜 Message 正在父表单和托管控件级别下进行处理,并且不会冒泡到我可以拦截甚至处理它的位置。有没有办法做到这一点,或者有其他方法来使用 CTRL-MouseWheel 模拟放大和缩小?

感谢您的阅读!

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!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

迷路的信 2024-10-05 08:15:28

首先将 WebBrowser.Document.DomDocument 转换为 mshtml 命名空间中的正确接口,例如 mshtml.HTMLDocumentEvents2_Event,然后您可以处理(和取消)鼠标滚轮事件。我不确定,但我认为您需要在文档更改时连接事件处理程序,因此我在 WebBrowser.DocumentCompleted 事件上执行此操作。我也不确定您是否需要释放任何 COM 对象。

这太令人沮丧了,我让它工作并不再关心......

这里至少有一份解释基础知识的文档:如何在 Visual C# .NET 应用程序中处理文档事件

对于您的具体情况,只需根据 CTRL 是否有条件地压缩 onmousewheel 事件即可键被按下。

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
} 

现在,如果您需要知道滚轮增量(滚动量),您将需要将事件对象转换为另一个接口。

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

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;
    [...]
}
抱着落日 2024-10-05 08:15:28

也许使用 SetWindowsHookEx 来查找这些事件可能对您有用。这就是我用来获取 ActiveX MapPoint 控件顶部的滚轮事件的方法。

请注意,Windows 7 上存在一些怪癖,可能需要进行一些修改。有关更多详细信息,请在 MSDN 论坛上搜索 Windows 7 上的 SetWindowsHookEx。

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.

娜些时光,永不杰束 2024-10-05 08:15:28

要解决此问题,您必须侦听并处理以下消息:

  • OLECMDID_GETZOOMRANGE
  • OLECMDID_OPTICAL_GETZOOMRANGE
  • OLECMDID_OPTICAL_ZOOM
  • OLECMDID_ZOOM

它们由 Internet Explorer 调度。请参阅MSDN 上的注释。

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.

请别遗忘我 2024-10-05 08:15:28

这是我用来禁用 ctrl+shift 的代码:
您需要更改最深层控件“Internet Explorer_Server”中 WndProc 的行为,
在您的网络浏览器准备就绪后执行此操作:

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

您可以从 MSDN 找到有关 WM_MOUSEWHEEL 的更多消息。
我从 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.

怂人 2024-10-05 08:15:28

我无法让其中任何一个可靠地工作,因此经过一些(令人沮丧的)实验后,我想出了 TCC 发布的答案的衍生结果。我的网络浏览器控件托管在用户控件中。主要区别是我对 HTMLDocumentEvents2_Event 使用类级变量,这样我就可以成功取消订阅,并且我将 mshtml.IHTMLEventObj pEvtObj.Returnvalue 设置为 true.. 现在似乎工作得很好。

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

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;
    } 
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文