Search code examples
.netwinformsclickmouseback-button

Process mouse back and forward buttons in WinForms form when its client area is covered by controls


I have a WinForms app written for the classical .NET Framework (namely v4.7.2). I need to process the mouse back and forward buttons in one of the forms. The problem is that the form is fully covered by other controls like SplitContainer and Panels, and I could not find a way to intercept the required buttons using standard native .NET techniques.

For example, a solution with overriding WndProc

private const int WM_XBUTTONDOWN = 0x020B;
private const int MK_XBUTTON1 = 0x0020;
private const int MK_XBUTTON2 = 0x0040;

protected override void WndProc(ref Message m)
{
    if (m.Msg == WM_XBUTTONDOWN)
    {
        int lowWord = (m.WParam.ToInt32() << 16) >> 16;
        switch (lowWord)
        {
            case MK_XBUTTON1:
                // navigate backward
                break;
            case MK_XBUTTON2:
                // navigate forward
                break;
        }
    }
    base.WndProc(ref m);
}

works only if the mouse pointer is over the part of the client area not covered by any control.

Is there a way to make all form controls 'transparent' for clicks of the mouse back/forward buttons using a .NET native technique and process those x-buttons in WndProc or a similar form method? If not, should I use a low-level mouse hook based on WinAPI to implement what I need? All solutions in C# or VB.NET are welcome.


Solution

  • A solution based on IMessageFilter does the work:

    public partial class Form1 : Form, IMessageFilter
    {
        public Form1()
        {
            Application.AddMessageFilter(this);
            this.FormClosed += (s, e) => Application.RemoveMessageFilter(this);
    
            InitializeComponent();
        }
    
        private const int WM_XBUTTONDOWN = 0x020B;
        private const int MK_XBUTTON1 = 0x0020;
        private const int MK_XBUTTON2 = 0x0040;
    
        public bool PreFilterMessage(ref Message m)
        {
            if (m.Msg == WM_XBUTTONDOWN)
            {
                int lowWord = (m.WParam.ToInt32() << 16) >> 16;
                switch (lowWord)
                {
                    case MK_XBUTTON1:
                        // navigate backward
                        BackButton_Click(null, null);
                        break;
                    case MK_XBUTTON2:
                        // navigate forward
                        ForwardButton_Click(null, null);
                        break;
                }
            }
            return false; // dispatch further
        }
    }