Prevent MouseWheel scrolling in the SplitterPanel of a SplitContainer

73 views Asked by At

I want to prevent scrolling with the MouseWheel in a SplitterPanel with a scrollbar conditionally, but preserve the scrolling behaviour (as shown in the code below) how can I do this?

I have this in the code, but it doesn't prevent scrolling and give weird behaviour:

public DocForm()
{
    InitializeComponent();

    this.splitContainer.Panel2.MouseWheel += new MouseEventHandler(this.splitContainer_Panel2_MouseWheel);
}

private void splitContainer_Panel2_MouseWheel(object sender, MouseEventArgs e)
{
    if ((ModifierKeys & Keys.Control) == Keys.Control)
    {
        float scaleDelta = e.Delta / SystemInformation.MouseWheelScrollDelta * 0.1f;
        // Scale instead of scrolling
        // ...
    }
    else
    {
        OnMouseWheel(e);
    }
}
2

There are 2 answers

0
Jimi On

You can implement IMessageFilter in a Custom Control derived from SplitContainer, to suppress WM_MOUSEHWHEEL (and WM_MOUSEHWHEEL, eventually), when the recipient of the message is one of the SplitterPanels (Panel2, here).

It's probably simpler (IMO), because the SplitterPanel class is sealed (it shadows a lot of properties of its base class).

Call Application.AddMessageFilter() to add the filter and Application.RemoveMessageFilter() to remove it:

using System.ComponentModel;
using System.Windows.Forms;

[ToolboxItem(true), DesignerCategory("code")]
public class SplitContainerNoWheel : SplitContainer, IMessageFilter {
    const int WM_MOUSEWHEEL = 0x20A;
    const int WM_MOUSEHWHEEL = 0x20E;
    public SplitContainerNoWheel() { }

    public bool PreFilterMessage(ref Message m) {
        if (m.Msg == WM_MOUSEWHEEL || m.Msg == WM_MOUSEHWHEEL) {
            // Check who's the intended recipient: the filter traps all messages
            if (Panel2.IsHandleCreated && m.HWnd == Panel2.Handle) return true;
        }
        return false;
    }

    protected override void OnHandleCreated(EventArgs e) {
        base.OnHandleCreated(e);
        Application.AddMessageFilter(this);
    }

    protected override void OnHandleDestroyed(EventArgs e) {
        Application.RemoveMessageFilter(this);
        base.OnHandleDestroyed(e);
    }
}

To replace the existing SplitContainer with this one, open the Designer.cs file of the parent Form, then replace the Field set to new SplitContainer() with new SplitContainerNoWheel() (or whatever name you decide to assign to this custom Control)

0
IV. On

One approach is to set a hook in your MainForm using IMessageFilter. The advantage of doing it here specifically is that it gives precise targeting over which specific controls, or control types, will receive special treatment of the mouse wheel events, and provides this at the application level without having to make unnecessary subclasses or custom controls.

public partial class MainForm : Form , IMessageFilter
{
    public MainForm()
    {
        InitializeComponent();
        Application.AddMessageFilter(this);
        Disposed += (sender, e) =>Application.RemoveMessageFilter(this);
    }

    int _debugCount = 1;
    public bool PreFilterMessage(ref Message m)
    {
        if (m.Msg == WM_MOUSEWHEEL)
        {
            // 'Generically' for SplitContainer or by any control placed within one.
            if (Control.FromHandle(m.HWnd) is Control control && localIsDescOfSplitContainer(control))
            {
#if DEBUG
                if (control is RichTextBox logger)
                {
                    logger.AppendText($"Count : {_debugCount++}{Environment.NewLine}");
                    logger.ScrollToCaret();
                }
#endif
                if (ModifierKeys == Keys.Control)
                {
                    int delta = (int)m.WParam >> 16;
                    if (delta > 0) control.Scale(new SizeF(1.1f, 1.1f));
                    else control.Scale(new SizeF(0.9f, 0.9f));
                    // Return here to suppress further actions ONLY
                    // for the ModifierKeys == Control case.
                    // m.Result = (IntPtr)1;
                    // return true;
                }
                // Or here to suppress ALL normal functioning of the scroll wheel.
                m.Result = (IntPtr)1;
                return true;
            }

            bool localIsDescOfSplitContainer(Control? aspirant)
            {
                while (aspirant != null)
                {
                    if (aspirant is SplitContainer) return true;
                    aspirant = aspirant.Parent;
                }
                return false;
            }
        }
        base.WndProc(ref m);
        return false;
    }
    private const int WM_MOUSEWHEEL = 0x020A;
}
    private const int WM_MOUSEWHEEL = 0x020A;
}

Example

init

In this example, a RichTextBox is placed on SplitContainer.Panel2. When the mouse wheel is scrolled, the effect on the text box is suppressed. You actually have to drag the VScrollBar to scroll the text box.

suppressed

But when the Control key is active, the size of the text box will scale.

scaling