Search code examples
c#.netwinformsuser-controls

Propagate event to all controls


I'm using the following piece of code, for documentation, error handling and/or logging. It's saves an image of the UserControl or Form when I click it pressing Control+Alt+Shift:

  public Image GetImage()
  {
     Bitmap oBmp = new Bitmap(this.Width, this.Height);
     this.DrawToBitmap(oBmp, new Rectangle(0, 0, oBmp.Width, oBmp.Height));
     return (Image)oBmp;
  }

  protected override void OnMouseDown(MouseEventArgs e)
  {
     base.OnMouseDown(e);

     bool bControl = false;
     bool bShift = false;
     bool bAlt = false;

     bControl = (Control.ModifierKeys & Keys.Control) == Keys.Control;
     bShift = (Control.ModifierKeys & Keys.Shift) == Keys.Shift;
     bAlt = (Control.ModifierKeys & Keys.Alt) == Keys.Alt;

     if (bControl && bShift && bAlt)
     {
        GetImage().Save(this.Name.TimedLocalFileName("png"), ImageFormat.Png);
     }
  }

Right now, I'm coding it in every UserControl, in the base form and so. It's easy to do because I'm using a code Snippet. But it has obvious setbacks.

  1. The same piece of code in a lot of places (maintainability); and
  2. Works only when I click on the base control and not it's childs (if an UserControl has a Label, this doesn't works.

I've been for a few days analyzing GlobalHooks (mostly here: CodeProject, but my head is not helping me.

Any suggestion will be very much appreciated.

Note: TimedLocalFileName is an extension method that returns a String in format <ControlName>_<Culture>_<YYYYMMDD>_<HHMMSS>.<FileExtension>


Solution

  • Create a base UserControl and name it BaseUserControl and derive all your user control from BaseUserControl then you can put all the logic inside the base user control.

    Inside the BaseUserControl, using a recursive method, handle MouseDown event of all child controls and redirect them to OnMouseDown of this, like this post.

    Override OnHanldeCrated and call that recursive method to wire up events.

    Here is the base control:

    using System;
    using System.Drawing;
    using System.Windows.Forms;
    public class BaseUserControl : UserControl
    {
        void WireMouseEvents(Control container)
        {
            foreach (Control c in container.Controls)
            {
                c.MouseDown += (s, e) =>
                {
                    var p = PointToThis((Control)s, e.Location);
                    OnMouseDown(new MouseEventArgs(e.Button, e.Clicks, p.X, p.Y, e.Delta));
                };
                WireMouseEvents(c);
            };
        }
        Point PointToThis(Control c, Point p)
        {
            return PointToClient(c.PointToScreen(p));
        }
        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);
            if (Control.ModifierKeys == (Keys.Control | Keys.Alt | Keys.Shift))
                MessageBox.Show("Handled!");
            // Your custom logic
        }
        protected override void OnHandleCreated(EventArgs e)
        {
            base.OnHandleCreated(e);
            WireMouseEvents(this);
        }
    }