C# - захват колеса CTRL-Mouse в элементе управления WebBrowser

Я разрабатываю приложение Windows Forms на C# со встроенным WebBrowser управление "фиктивным" (то есть отключение контекстных меню, кнопки "назад", бесплатная навигация и т. д.), доступ к стороннему веб-приложению.

Прямо сейчас я пытаюсь добавить функцию Zoom в свой браузер. У меня есть клавиатурные комбинации, работающие для этого (CTRL + и CTRL - сделать правильные вызовы OLE для базового объекта ActiveX WebBrowser), но среди других неприятных вещей о WebBrowser Мне приходилось иметь дело, я не могу понять, как захватить колесо CTRL-Mouse для имитации функции Zoom, как это делает IE. Я искал везде, чтобы найти решение этой проблемы, но безрезультатно.

Чтобы попытаться это выяснить, я создал пустую форму, содержащую только элемент управления WebBrowser, и обнаружил следующее:

  1. CTRL-MouseWheel всегда запускает MouseWheel событие, когда родительская форма имеет фокус и курсор мыши находится над верхней частью окна (например, над заголовком приложения), или когда курсор мыши находится над WebBrowser контроль, когда кажется, что он не имеет фокуса, даже если родительская форма имеет фокус.
  2. CTRL-MouseWheel никогда не запускает MouseWheel событие, когда курсор мыши находится над WebBrowser контролировать и WebBrowser имеет фокус, и, кажется, не имеет никакого эффекта.
  3. Использование колесика мыши без CTRL прокручивает содержимое окна WebBrowser но не стреляет MouseWheel событие до тех пор, пока вертикальная полоса прокрутки не достигнет верхней или нижней части.
  4. Перехват Message за WM_MOUSEWHEEL переопределив WndProc а также DefWndProc как для образца класса, унаследованного от WebBrowser и для родительской формы применяется только для вышеуказанных условий (с wParam правильно обозначает MK_CONTROL).
  5. PreviewKeyDown событие запускается при нажатии CTRL, как и ожидалось, но все равно ничего не делает в унисон с колесом мыши.

Так что я думаю, Message обрабатывается ниже родительской формы и уровня управляемого управления и не всплывает там, где я могу перехватить или даже обработать ее. Есть ли способ сделать это или какой-то другой способ имитировать увеличение и уменьшение масштаба с помощью CTRL-MouseWheel?

Спасибо за прочтение!

5 ответов

Сначала бросьте WebBrowser.Document.DomDocument на правильный интерфейс в пространстве имен mshtml, как mshtml.HTMLDocumentEvents2_Event, тогда вы можете обрабатывать (и отменять) события колесика мыши. Я не уверен, но я думаю, что вам нужно подключать обработчик событий каждый раз, когда документ изменяется, поэтому я делаю это на WebBrowser.DocumentCompleted событие. Я также не уверен, если вам нужно освободить какие-либо объекты COM.

Это было достаточно разочаровывающим, что я заставил его работать и перестал заботиться...

Вот хотя бы один документ, объясняющий основы: Как обрабатывать события документа в приложении Visual C# .NET

Для вашего конкретного случая, просто условно раздавить onmousewheel событие, основанное на том, CTRL клавиша нажата.

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;
    [...]
}

Я не мог заставить что-либо из них работать надежно, поэтому после некоторых (разочаровывающих) экспериментов я получил производную от ответа, опубликованного 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;
    } 

Чтобы решить эту проблему, вы должны прослушать и обработать следующие сообщения:

  • OLECMDID_GETZOOMRANGE
  • OLECMDID_OPTICAL_GETZOOMRANGE
  • OLECMDID_OPTICAL_ZOOM
  • OLECMDID_ZOOM

Они отправлены Internet Explorer. Смотрите замечания по MSDN.

Это код, который я использовал для отключения Ctrl+ Shift:
Вам нужно изменить поведение WndProc в самом глубоком элементе управления "Internet Explorer_Server",
Сделайте это после того, как ваш веб-браузер будет готов:

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

Вы можете найти больше сообщений о WM_MOUSEWHEEL от MSDN.
Я нахожу это из MSDN. Но я забыл ссылку, Как только найду, добавлю ее сюда.

Возможно, использование SetWindowsHookEx для поиска этих событий может работать для вас. Это то, что я использовал для получения событий колеса прокрутки поверх элемента управления ActiveX MapPoint.

Имейте в виду, что в Windows 7 есть некоторые причуды, которые могут потребовать некоторой обработки. Для получения дополнительной информации выполните поиск SetWindowsHookEx на Windows 7 на форумах MSDN.

Другие вопросы по тегам