Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handling a click event anywhere inside a panel in C#

Tags:

c#

winforms

I have a panel in my Form with a click event handler. I also have some other controls inside the panel (label, other panels, etc.). I want the click event to register if you click anywhere inside the panel. The click event works as long as I don't click on any of the controls inside the panel but I want to fire the event no matter where you click inside the panel. Is this possible without adding the same click event to all of the controls inside the panel?

like image 782
Scott Chantry Avatar asked Oct 13 '10 18:10

Scott Chantry


2 Answers

Technically it is possible, although it is very ugly. You need to catch the message before it is sent to the control that was clicked. Which you can do with IMessageFilter, you can sniff an input message that was removed from the message queue before it is dispatched. Like this:

using System;
using System.Drawing;
using System.Windows.Forms;

class MyPanel : Panel, IMessageFilter {
    public MyPanel() {
        Application.AddMessageFilter(this);
    }
    protected override void Dispose(bool disposing) {
        if (disposing) Application.RemoveMessageFilter(this);
        base.Dispose(disposing);
    }
    public bool PreFilterMessage(ref Message m) {
        if (m.HWnd == this.Handle) {
            if (m.Msg == 0x201) {  // Trap WM_LBUTTONDOWN
                Point pos = new Point(m.LParam.ToInt32());
                // Do something with this, return true if the control shouldn't see it
                //...
                // return true
            }
        }
        return false;
    }
}
like image 77
Hans Passant Avatar answered Nov 16 '22 13:11

Hans Passant


I needed the exact same functionality today, so this is tested and works:

1: Create a subclasser which can snatch your mouseclick:

internal class MessageSnatcher : NativeWindow
{
    public event EventHandler LeftMouseClickOccured = delegate{};

    private const int WM_LBUTTONDOWN = 0x201;
    private const int WM_PARENTNOTIFY = 0x210;

    private readonly Control _control;

    public MessageSnatcher(Control control)
    {
        if (control.Handle != IntPtr.Zero)
            AssignHandle(control.Handle);
        else
            control.HandleCreated += OnHandleCreated;

        control.HandleDestroyed += OnHandleDestroyed;
        _control = control;
    }

    protected override void WndProc(ref Message m)
    {
        if (m.Msg == WM_PARENTNOTIFY)
        {
            if (m.WParam.ToInt64() == WM_LBUTTONDOWN)
                LeftMouseClickOccured(this, EventArgs.Empty);
        }

        base.WndProc(ref m);
    }

    private void OnHandleCreated(object sender, EventArgs e)
    {
        AssignHandle(_control.Handle);
    }

    private void OnHandleDestroyed(object sender, EventArgs e)
    {
        ReleaseHandle();
    }
}

2: Initialize the snatcher to hook into the panel WndProc:

    private MessageSnatcher _snatcher;

    public Form1()
    {
        InitializeComponent();

        _snatcher = new MessageSnatcher(this.panel1);
    }

3: The Message snatcher will get the WM_PARENTNOTIFY if you click on a child control.

like image 37
Marius Avatar answered Nov 16 '22 11:11

Marius