How do I close a form when a user clicks outside the form's window?

19,909

Solution 1

With thanks to p-daddy in this question, I've found this solution which allows me to use ShowDialog:

protected override void OnShown(EventArgs e)
{
    base.OnShown(e);
    this.Capture = true;
}

protected override void OnCaptureChanged(EventArgs e)
{
    if (!this.Capture)
    {
        if (!this.RectangleToScreen(this.DisplayRectangle).Contains(Cursor.Position))
        {
            this.Close();
        }
        else
        {
            this.Capture = true;
        }
    }

    base.OnCaptureChanged(e);
}

Solution 2

In your form's Deactivate event, put "this.Close()". Your form will close as soon as you click anywhere else in Windows.

Update: I think what you have right now is a Volume button, and inside the Click event you create an instance of your VolumeSlider form and make it appear by calling ShowDialog() which blocks until the user closes the popped-up form. In the next line you read the volume the user selected and use it in your program.

This is OK, but as you've noticed it forces the user to explicitly close the popup in order to get back to the main program. Show() is the method you really want to use here on your popup form, but Show() doesn't block which means the Click event back on your main form finishes without knowing what the new volume is supposed to be.

A simple solution is to create a public method on your main form like this:

public void SetVolume(int volume)
{
    // do something with the volume - whatever you did before with it
}

Then, in your Volume button's Click event (also on the main form), you make the VolumeSlider appear like so:

VolumeSlider slider = new VolumeSlider();
slider.Show(this); // the "this" is needed for the next step

In the VolumeSlider form, as the user works the (I guess) scrollbar, you put this code in the scrollbar's ValueChanged event (I think that's what it is):

MainForm owner = (MainForm)this.Owner;
owner.SetVolume(scrollbar.Value);

And then in the VolumeSlider form's Deactivate event you would put this.Close() as mentioned above. Your form will then behave as expected.

Solution 3

With Simon's solution I had the same Problem describt by Noam. With following code I've avoid the "Click through" problem...

protected override void WndProc(ref Message m)
{    
    base.WndProc(ref m);

    // if click outside dialog -> Close Dlg
    if (m.Msg == NativeConstants.WM_NCACTIVATE) //0x86
    {
        if (this.Visible)
        {
            if (!this.RectangleToScreen(this.DisplayRectangle).Contains(Cursor.Position))
                this.Close();
        }
    }
}

Solution 4

You should use Deactivate:
I have a form called Form2.
In the Deactivate section of the Properties window.
You declare the name Form2_Deactivate.
In the Form2.cs file:

private void Form2_Deactivate(object sender, EventArgs e)
{
    this.Close();
}
Share:
19,909
Simon
Author by

Simon

Updated on June 29, 2022

Comments

  • Simon
    Simon almost 2 years

    I want to close a System.Windows.Forms.Form if the user clicks anywhere outside it. I've tried using IMessageFilter, but even then none of the messages are passed to PreFilterMessage. How do I receive clicks outside a form's window?

  • Simon
    Simon over 15 years
    it's a volume slider - that seems to be the way everyone else has implemented them.
  • MusiGenesis
    MusiGenesis over 15 years
    This isn't too different from a drop-down box - the choice list goes away as soon as you click somewhere else.
  • MusiGenesis
    MusiGenesis over 15 years
    Me: "Doctor, it hurts when I do this." Doctor: "Don't do that." See my updates above in a second.
  • MusiGenesis
    MusiGenesis over 15 years
    Let me know if it works for you. I hate to be snarky and wrong at the same time. :)
  • Noam Gal
    Noam Gal over 14 years
    Thanks. Just what I was looking for.
  • Noam Gal
    Noam Gal over 14 years
    mm... It's not working as expected after all - when the form loads, I have the "hand" mouse cursor, and any click outside the form closes it. If I want to interact with the form itself, the first click only seems to clear the "Capture", and does not register inside the form (clicking on a checkbox does not check it, for example), and afterwards clicking outside does not close the window. Is there some other way to do this?
  • Simon
    Simon over 14 years
    @Noam: how simple is your form? If it's just got one child, you could set the child's capture rather than the form's.
  • Ashraf Bashir
    Ashraf Bashir about 12 years
    It's OnMouseCaptureChanged not OnCaptureChanged
  • Andrew Barber
    Andrew Barber almost 12 years
    Could you provide some context on this answer to enable someone to use it properly?
  • MusiGenesis
    MusiGenesis about 11 years
    @JesonPark: sure it does: msdn.microsoft.com/en-us/library/…
  • AminM
    AminM about 11 years
    tnx MusiGenesis i find it!
  • Bitterblue
    Bitterblue almost 10 years
    This is the truly correct answer. When you click outside of a form that was opened with ShowDialog(), 3 messages are sent: WM_NCACTIVATE, WM_ACTIVATE and WM_ACTIVATEAPP. Don't use the 3rd one but the 2nd (WM_ACTIVATE) might be even better than the 1st one. Also don't forget to set DialogResult!
  • Pablo Jomer
    Pablo Jomer over 9 years
    I used OnVisibleChanged and OnMouseCaptreChanged and it works but some times the mouse is hidden.
  • Patapom
    Patapom about 2 years
    This is what worked for me! Other solutions are just clunky, especially the one using OnDeactivate() that makes my entire application give focus to an entirely other application when closing the child form, for some reason (??)...