C# - захват колеса CTRL-Mouse в элементе управления WebBrowser
Я разрабатываю приложение Windows Forms на C# со встроенным WebBrowser
управление "фиктивным" (то есть отключение контекстных меню, кнопки "назад", бесплатная навигация и т. д.), доступ к стороннему веб-приложению.
Прямо сейчас я пытаюсь добавить функцию Zoom в свой браузер. У меня есть клавиатурные комбинации, работающие для этого (CTRL + и CTRL - сделать правильные вызовы OLE для базового объекта ActiveX WebBrowser), но среди других неприятных вещей о WebBrowser
Мне приходилось иметь дело, я не могу понять, как захватить колесо CTRL-Mouse для имитации функции Zoom, как это делает IE. Я искал везде, чтобы найти решение этой проблемы, но безрезультатно.
Чтобы попытаться это выяснить, я создал пустую форму, содержащую только элемент управления WebBrowser, и обнаружил следующее:
- CTRL-MouseWheel всегда запускает
MouseWheel
событие, когда родительская форма имеет фокус и курсор мыши находится над верхней частью окна (например, над заголовком приложения), или когда курсор мыши находится надWebBrowser
контроль, когда кажется, что он не имеет фокуса, даже если родительская форма имеет фокус. - CTRL-MouseWheel никогда не запускает
MouseWheel
событие, когда курсор мыши находится надWebBrowser
контролировать иWebBrowser
имеет фокус, и, кажется, не имеет никакого эффекта. - Использование колесика мыши без CTRL прокручивает содержимое окна
WebBrowser
но не стреляетMouseWheel
событие до тех пор, пока вертикальная полоса прокрутки не достигнет верхней или нижней части. - Перехват
Message
заWM_MOUSEWHEEL
переопределивWndProc
а такжеDefWndProc
как для образца класса, унаследованного отWebBrowser
и для родительской формы применяется только для вышеуказанных условий (сwParam
правильно обозначаетMK_CONTROL
). -
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.