Catching a Contained Control's Mouse Events in

2019-08-30 03:45发布

问题:

I have a UserControl that contains another UserControl. I'd like the containing control to be able to handle any mouse events that happen over the contained control's area. What's the easiest way to do this?

Changing code for the contained control is possible, but only as a last resort. The contained control has a window that is controlled by an unmanaged library.

FWIW, I have tried adding handlers for the contained control's mouse events, but those handlers never get called. I suspect the contained control is consuming the mouse events.

I've considered adding some sort of transparent window on top of the contained control, to catch the events, but I'm still pretty new to Windows Forms and I'm wondering if there is a better way.

回答1:

If the inside control is not sealed, you may want to subclass it and override the mouse-related methods:

protected override void OnMouseClick(MouseEventArgs e) {
    //if you still want the control to process events, uncomment this:
    //base.OnMouseclick(e)

    //do whatever
}

etc.



回答2:

Well, it's technically possible. You have to redirect the mouse message yourself, that requires a bit of P/Invoke. Paste this code into your inner UserControl class:

    protected override void WndProc(ref Message m) {
        // Re-post mouse messages to the parent window
        if (m.Msg >= 0x200 && m.Msg <= 0x209 && !this.DesignMode && this.Parent != null) {
            Point pos = new Point(m.LParam.ToInt32() & 0xffff, m.LParam.ToInt32() >> 16);
            // Fix mouse position to be relative from parent's client rectangle
            pos = this.PointToScreen(pos);
            pos = this.Parent.PointToClient(pos);
            IntPtr lp = (IntPtr)(pos.X + pos.Y << 16);
            PostMessage(this.Parent.Handle, m.Msg, m.WParam, lp);
            return;
        }
        base.WndProc(ref m);
    }

    [System.Runtime.InteropServices.DllImport("user32.dll")]
    private static extern IntPtr PostMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);

This is best avoided btw. The parent control should probably just subscribe to the inner control's mouse events.



回答3:

Here's what I did:

First, I defined a TransparentControl class, which is simply a control that is transparent and doesn't draw anything. (This code is thanks to http://www.bobpowell.net/transcontrols.htm.)

public class TransparentControl : Control
{
    protected override CreateParams CreateParams
    {
        get
        {
            CreateParams cp = base.CreateParams;
            cp.ExStyle |= 0x00000020; //WS_EX_TRANSPARENT
            return cp;
        }
    }

    protected override void OnPaint(PaintEventArgs pe)
    {
        // Do nothing
    }

    protected override void OnPaintBackground(PaintEventArgs pevent)
    {
        // Do nothing
    }
}

Then, I placed a TransparentControl in my user control on top of the contained user control, and added handlers for its mouse events.