2

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.

Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
Thomas Jomphe
  • 117
  • 6
  • 17
  • Are you looking to close the whole application as part of this, or simply the forms you have opened? – Martin Nov 28 '19 at 07:55
  • Only the forms opened from the panel – Thomas Jomphe Nov 28 '19 at 07:55
  • How does the main form get the timer event while there's a modal dialog showing? – Luaan Nov 28 '19 at 08:23
  • @Luaan If a control has a timer, it still receives messages from it even if there's a modal dialog open. – Matthew Watson Nov 28 '19 at 09:06
  • @ThomasJomphe Could you please post a [MCVE] to show what you mean exactly by parent panel? Because a if the panel is the owner of the forms, it means the form is not top level, so it couldn't be shown by `ShowDialog`. Could you make it clearer? – Reza Aghaei Dec 01 '19 at 04:17
  • @RezaAghaei. Sorry, my english is not very clear. First, the main form of my application is creating a panel than is shown in the main form. This panel create a form and show it with the ShowDialog method. Meanwhile, and after a few minutes, a timer in the main form calls a method in the main form that replace the existing panel with another one (not the same panel). The main form calls the Dispose method on the panel, and shows the new panel, but the dialog form is still on screen. I want to close it, but the code placed after the ShowDialog method should not be executed. – Thomas Jomphe Dec 01 '19 at 04:47
  • *This panel create a form and show it with the ShowDialog method.* → How a `Panel` create a form? Do you click on the panel or on a button inside that panel? – Reza Aghaei Dec 01 '19 at 04:50
  • @RezaAghaei. I create the form and show it by clicking a button in the panel. – Thomas Jomphe Dec 01 '19 at 04:51
  • Sorry, it is not a panel, but a UserControl. – Thomas Jomphe Dec 01 '19 at 04:52
  • Ok, this means the `Panel` or the `UserControl` is not really the parent of the form, It's just hosting the button which opens the form using `ShowDialog`. You can easily subscribe to `Disposed` method of the `UserControl` and close the form. I'll share an example. – Reza Aghaei Dec 01 '19 at 04:53
  • @RezaAghaei Exactly. – Thomas Jomphe Dec 01 '19 at 04:54
  • @RezaAghaei, now, i use this code to destroy the panel, but the form stays on the screen : if (pPanel != this._currentPanel) { this.mainPanel.Controls.Remove(this._currentPanel); if (this._currentPanel != null) { this._currentPanel.Dispose(); } panelToShow.Dock = DockStyle.Fill; this.mainPanel.Controls.Add(panelToShow); this._currentPanel = panelToShow; } – Thomas Jomphe Dec 01 '19 at 04:58
  • In general I'm not sure about the requirement which pushes you to recreate the `UserControl` or show a form as `Dialog` and then forcibly close it using a timer. To me it's more like an [XY problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). But anyhow, I've share an answer including some point to show you how you can rely on some existing events like `Disposed` or add some new events and use it like `OKSelected`. – Reza Aghaei Dec 01 '19 at 10:37
  • You could close gracefully *all* modal dialogs with a code like this: https://stackoverflow.com/a/7926997/403671 before disposing anything. – Simon Mourier Dec 01 '19 at 18:02

2 Answers2

1

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);
            }
        }
    }
}
Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
  • If there is some code after the f.ShowDialog();, will this code be executed ? – Thomas Jomphe Dec 01 '19 at 05:09
  • You should not put any code after `ShowDialog` as it will be blocked by the dialog. That's why I put the `this.Disposed += (obj, args) => { f.Close(); f.Dispose(); };` before `ShowDialog`. – Reza Aghaei Dec 01 '19 at 05:10
  • Yes, but sometimes I need to verify something from the form, like a return value or the DialogResult property. But when the timer calls the dispose method, I want to stop the execution and close the form, without executing the code after the ShowDialog. – Thomas Jomphe Dec 01 '19 at 05:13
  • It doesn't mean you cannot have code after show dialog. You can. Just check the dialog result. Also in disposed event check if the form is not disposed. – Reza Aghaei Dec 01 '19 at 05:38
  • And if I understand correctly, there is no easy way to stop the execution of the code in the usercontrol when the usercontrol.Dispose() is called (except checking the dialog result or any property) ? – Thomas Jomphe Dec 01 '19 at 05:44
  • I added more details to the answer. – Reza Aghaei Dec 01 '19 at 10:31
0

Can you modify the forms that you want to close automatically? If so, try adding the following to each form:

protected override void OnShown(EventArgs e)
{
    base.OnShown(e);

    if (this.Owner != null)
        this.Owner.HandleDestroyed += onOwnerHandleDestroyed;
}

void onOwnerHandleDestroyed(object sender, EventArgs e)
{
    this.Close();
}

NOTE: You are already using Dispose() to close the main form, so this should work. If, however, you used Close() to close the main form, then it wouldn't work because Close() doesn't close a form if it is the parent of any modal dialog.

Matthew Watson
  • 104,400
  • 10
  • 158
  • 276
  • I would prefer not modify all my forms. I also want this to stop the execution in the panel, so the code after the showDialog() will not be executed. – Thomas Jomphe Nov 28 '19 at 23:06