Is there any way to detect a mouseclick outside a user control?

Bildsoe picture Bildsoe · Apr 18, 2011 · Viewed 31.1k times · Source

I'm creating a custom dropdown box, and I want to register when the mouse is clicked outside the dropdown box, in order to hide it. Is it possible to detect a click outside a control? or should I make some mechanism on the containing form and check for mouseclick when any dropdownbox is open?

user control

Answer

Cody Gray picture Cody Gray · Apr 18, 2011

So I finally understand that you only want it to close when the user clicks outside of it. In that case, the Leave event should work just fine... For some reason, I got the impression you wanted it to close whenever they moved the mouse outside of your custom dropdown. The Leave event is raised whenever your control loses the focus, and if the user clicks on something else, it will certainly lose focus as the thing they clicked on gains the focus.

The documentation also says that this event cascades up and down the control chain as necessary:

The Enter and Leave events are hierarchical and will cascade up and down the parent chain until the appropriate control is reached. For example, assume you have a Form with two GroupBox controls, and each GroupBox control has one TextBox control. When the caret is moved from one TextBox to the other, the Leave event is raised for the TextBox and GroupBox, and the Enter event is raised for the other GroupBox and TextBox.

Overriding your UserControl's OnLeave method is the best way to handle this:

protected override void OnLeave(EventArgs e)
{
   // Call the base class
   base.OnLeave(e);

   // When this control loses the focus, close it
   this.Hide();
}

And then for testing purposes, I created a form that shows the drop-down UserControl on command:

public partial class Form1 : Form
{
   private UserControl1 customDropDown;

   public Form1()
   {
      InitializeComponent();

      // Create the user control
      customDropDown = new UserControl1();

      // Add it to the form's Controls collection
      Controls.Add(customDropDown);
      customDropDown.Hide();
   }

   private void button1_Click(object sender, EventArgs e)
   {         
      // Display the user control
      customDropDown.Show();
      customDropDown.BringToFront();   // display in front of other controls
      customDropDown.Select();         // make sure it gets the focus
   }
}

Everything works perfectly with the above code, except for one thing: if the user clicks on a blank area of the form, the UserControl doesn't close. Hmm, why not? Well, because the form itself doesn't want the focus. Only controls can get the focus, and we didn't click on a control. And because nothing else stole the focus, the Leave event never got raised, meaning that the UserControl didn't know it was supposed to close itself.

If you need the UserControl to close itself when the user clicks on a blank area in the form, you need some special case handling for that. Since you say that you're only concerned about clicks, you can just handle the Click event for the form, and set the focus to a different control:

protected override void OnClick(EventArgs e)
{
   // Call the base class
   base.OnClick(e);

   // See if our custom drop-down is visible
   if (customDropDown.Visible)
   {
      // Set the focus to a different control on the form,
      // which will force the drop-down to close
      this.SelectNextControl(customDropDown, true, true, true, true);
   }
}

Yes, this last part feels like a hack. The better solution, as others have mentioned, is to use the SetCapture function to instruct Windows to capture the mouse over your UserControl's window. The control's Capture property provides an even simpler way to do the same thing.