Search code examples
c#.netwinformstimerdispose

Close the modal form which is opened by a button of a UserControl when I dispose the UserControl


I have an application in which I need to make sure the form opened by click on a button on a user control using ShowDialog(), will be closed and disposed when I dispose the user control.

I'm calling userControl.Dispose() in my main form through a timer.

Is there a way I can do that ?

Thanks...

Here is more details about the flow of the forms:

The MainForm of my application is creating a UserControl which has a Button. Than when the user clicks on the button of the user control, it shows a model form using ShowDialog.

Meanwhile, and after a few minutes, a timer in the main form replaces the existing user control with another instance of the user control. The main form calls the Dispose method of the previous user control, and the shows the new on.

But the problem is the modal dialog is still open on screen, blocking the main form. I want to close it, and the code placed after the ShowDialog method should not be executed.


Solution

  • Short answer

    You can subscribe Disposed event of your UserControl and close the form which it shows. Regarding to the comments under the question, it looks like you have a UserControl containing a Button and in Click event of the button, you show a Form using ShowDialog().

    To close and dispose the form, you need to subscribe Disposed event of your UserControl before showing the form as dialog.

    More details

    If you want to decide to run some logic depending to the dialog result of the form, you can check the dialog result and if it's OK, run the custom logic which you need.

    To enhance the flow a bit, you can define some events and properties in your user control and handle them in the main form:

    • OKSelected event, and you can raise it immediately after closing the dialog if the dialog result is OK. It will let you to handle this event in the main form, for example to stop the timer if the user clicked OK in dialog.
    • ProcessingFinished, and you can raise it after you finished some processing after closing the dialog when the dialog result is OK. You can handle this in main form, for example to start the timer again.
    • You can define some properties in case you want to communicate some values with the main form.

    Here is an example of the code in main form:

    MyUserControl uc = null;
    private void timer1_Tick(object sender, EventArgs e)
    {
        if (!(uc == null || uc.IsDisposed || uc.Disposing))
        {
            this.Controls.Remove(uc);
            uc.Dispose();
        }
        uc = new MyUserControl();
        this.Controls.Add(uc);
        uc.OKSelected += (obj, args) => { timer1.Stop(); };
        uc.ProcessingFinished += (obj, args) =>
        {
            MessageBox.Show(uc.Info);
            timer1.Start();
        };
    }
    

    And here is an example of the user control:

    public partial class MyUserControl : UserControl
    {
        public MyUserControl() { InitializeComponent(); }
        public EventHandler OKSelected;
        public EventHandler ProcessingFinished;
        public string Info { get; private set; }
        private void button1_Click(object sender, EventArgs e)
        {
            using (var f = new Form()) {
                var button = new Button() { Text = "OK" };
                f.Controls.Add(button);
                button.DialogResult = DialogResult.OK;
                this.Disposed += (obj, args) => {
                    if (!(f.IsDisposed || f.Disposing)) {
                        f.Close(); f.Dispose();
                    }
                };
                if (f.ShowDialog() == DialogResult.OK) {
                    //If you need, raise the OKSelected event
                    //So you can handle it in the main form, for example to stop timer
                    OKSelected?.Invoke(this, EventArgs.Empty);
                    //
                    //Do whatever you need to do after user closed the dialog by OK
                    //
                    //If you need, raise the ProcessingFinished event
                    //So you can handle it in the main form, for example to start timer
                    //You can also set some properties to share information with main form
                    Info = "something";
                    ProcessingFinished?.Invoke(this, EventArgs.Empty);
                }
            }
        }
    }