9

The WebBrowser control in WPF is inherited from theUIElement, but we cannot register event handlers in UIElement events. Why is it? At WPF WebBrowser Mouse Events not working as expected, it is answered but I still cannot understand.

Anyway, hooking up handlers to the events provided by the document of the WebBrowser can catch most mouse events but cannot use 'Back' & 'Forward' navigation button events. Since the internet explorer can do this, I think it is possible. Is there any way to solve this issue?

UPDATE: In this question, 'Back' & 'Forward' navigation buttonsmean XButton1 and XButton2 in 5-button mouse system.

UPDATE2: I fixed this question with the Navid Rahmani's answer. I'd think someone will need this answer, so I attach main part. If finding any problem or more reasonable solution, please let me know.

    //This code assumes the `WebBrowser` field named _webBrowser is already initiated.
    //For the detail out of this code, please refer to the Navid Rahmani's answer.

    private bool _isMouseOver;
    private HTMLDocumentEvents2_Event _docEvent;    

    public ctor()
    {
        _webBrowser.LoadCompleted += _webBrowser_LoadCompleted;
    }

    private void _webBrowser_LoadCompleted(object sender, NavigationEventArgs e)
    {
        if (_docEvent != null)
        {
            _docEvent.onmouseover -= _docEvent_onmouseover;
            _docEvent.onmouseout -= _docEvent_onmouseout;
        }
        if (_webBrowser.Document != null)
        {
            _docEvent = (HTMLDocumentEvents2_Event)_webBrowser.Document;
            _docEvent.onmouseover += _docEvent_onmouseover;
            _docEvent.onmouseout += _docEvent_onmouseout;
        }
    }

    void _docEvent_onmouseout(IHTMLEventObj pEvtObj)
    {
        _isMouseOver = false;
    }

    void _docEvent_onmouseover(IHTMLEventObj pEvtObj)
    {
        _isMouseOver = true;
    }


    private IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
    {
        if (_isMouseOver)
        {
            if (nCode >= 0 && (MouseMessages)wParam == MouseMessages.XBUTTON)
            {
                var hookStruct = (Msllhookstruct)Marshal.PtrToStructure(lParam, typeof(Msllhookstruct));
                if (hookStruct.mouseData == 0x10000)
                {
                    //do something when XButto1 clicked
                }
                else if (hookStruct.mouseData == 0x20000)
                {
                    //do something when XButto2 clicked
                }
            }
        }
        return CallNextHookEx(_hookID, nCode, wParam, lParam);
    }


    private enum MouseMessages
    {
        //WM_LBUTTONDOWN = 0x00A1,
        //WM_LBUTTONUP = 0x0202,
        //WM_MOUSEMOVE = 0x0200,
        //WM_MOUSEWHEEL = 0x020A,
        //WM_RBUTTONDOWN = 0x0204,
        //WM_RBUTTONUP = 0x0205,
        XBUTTON = 0x020B,
    }
Community
  • 1
  • 1
Jin-Wook Chung
  • 4,196
  • 1
  • 26
  • 45

4 Answers4

4

Much simpler way....

This works for me in WPF and .net 4.5

private void Window_MouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton.Equals(MouseButton.XButton1)) MessageBox.Show(@"back");
if (e.ChangedButton.Equals(MouseButton.XButton2)) MessageBox.Show(@"forward");
}
Gary93730
  • 431
  • 4
  • 13
  • 1
    The MouseDown and PreviewMouseDown are swallowed by the WebBrowser control, so it did not work for me. – Dave Jul 18 '14 at 21:42
2

You can use low level hook of mouse and check if the xbutton1 or xbutton2 clicked
look here

for the value of WM_XBUTTONDOWN look http://msdn.microsoft.com/en-us/library/ms646245(VS.85).aspx

Navid Rahmani
  • 7,848
  • 9
  • 39
  • 57
  • I tried as your answer, but the `WM_LBUTTONDOWN`event was cool and the `XBUTTON`event was not. I tried `0x0001 and 0x0020`as the value of `MouseMessages enum`for the `XBUTTON`event. I made something wrong? – Jin-Wook Chung Jun 18 '11 at 17:26
  • did you checked the high order text? look at this link http://msdn.microsoft.com/en-us/library/ms997498.aspx – Navid Rahmani Jun 18 '11 at 17:40
  • No you should check `0x020C` and for button check high order word parameter `0x0001` and `0x0002` – Navid Rahmani Jun 18 '11 at 17:41
  • I would like to thank for your comment. I achieved the use of the mouse navigation events with `0x020B` as the `MouseMessages enum` and, `0x10000` for the left `XBUTTON` and `0x20000` for the right `XBUTTON` as the `hookStruct.mouseData`. However, the mouse navigation events is handled if the events is fired anywhere in the windows. – Jin-Wook Chung Jun 18 '11 at 19:32
  • You can check if the mouse is over the document. – Navid Rahmani Jun 18 '11 at 19:38
  • Is there any way only the events are handled in the target region of my main view? – Jin-Wook Chung Jun 18 '11 at 19:40
  • use one of these `(UIElement)Mouse.DirectlyOver;` or `element.IsMouseOver` – Navid Rahmani Jun 18 '11 at 19:44
  • I fixed all of this issue. Thanks a lot. I'll give you the bounty after the limitation time. I couldn't use the `IsMouseOver` of the WebBrowser directly, which is the same problem with this question. So, I used the `HTMLDocumentEvents2_Event.onmouseover&onmouseout` events to set the mouse over state. – Jin-Wook Chung Jun 18 '11 at 20:36
0

The WebBrowser controll is really just thin wrapper around the Trident COM object. It's not 'pure WPF' like other built-in controls... so lots of normal things don't work with it. To answer your question, the closest you can get is hooking into the Navigating event. This won't tell you whether the user is trying to go forwards or back or elsewhere but it will give you the URL and the opportunity to set e.Cancel = true to stop the navigation (usually followed by calling Navigate(url) to take the user somewhere else).

Robert Levy
  • 28,747
  • 6
  • 62
  • 94
  • First, I would like to thank for your answer. However, regardless of a direction, I cannot register a event handle to the navigating event of the `WebBrowser` with the mouse `XButton` click. – Jin-Wook Chung Jun 18 '11 at 10:07
0

TL;DR

Install my nuget package Lette.Wpf.AppCommands (source on GitHub) and add this to your main window:

<l:Window.AppCommandBindings>
    <AppCommandBinding AppCommand="BrowserBackward" Command="{Binding BackCommand}" />
</l:Window.AppCommandBindings>

The BackCommand will be invoked whenever the "back" button (whichever it is) is pressed.

Explanation

The XButton1 and XButton2 may default to being mapped to Back and Forward navigation, but this may not always be the case. Those buttons can be remapped to other functions, and some mice have more than five buttons. So, how do you actually listen for the Backwards navigation message?

  1. Hook up a WndProc.
  2. Listen for the WM_APPCOMMAND message.
  3. In the lparam, do some bit-twiddling and find the actual APPCOMMAND_* value.
  4. If it is the desired value (eg. APPCOMMAND_BROWSER_BACKWARD), do your thing.

Example:

var hwndSource = HwndSource.FromHwnd(new WindowInteropHelper(mainWindow).Handle);
hwndSource.AddHook(WndProc);

// ...

private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam, ref bool handled)
{
    if (msg == WM_APPCOMMAND)
    {
        var appCommand = BitTwiddling(lparam);

        if (appCommand == APPCOMMAND_BROWSER_BACKWARD)
        {
            // do your thing
        }
    }
    return IntPtr.Zero;
}

The mainWindow is a reference to your main window (of type Window).

The BitTwiddling is basically: convert the IntPtr to an int, shift right 16 bits then mask with 0x7FFF.

Christoffer Lette
  • 14,346
  • 7
  • 50
  • 58