Any VB.Net programmers? Infinite recursion error when closing

My program ends when Form1 closes. However, the program closes without calling the FormClosed events of Form2 and Form3. This is bad, because I need to make sure the data in Form2 and Form3 is saved.

So I add code to make sure those forms are properly closed:


Private Sub Form1_FormClosed(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed
        SaveForm1Data()
        Form2.Close()
        Form3.Close()
End Sub

Private Sub Form2_FormClosed(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed
        SaveForm2Data()
End Sub

Private Sub Form3_FormClosed(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed
        SaveForm3Data()
End Sub

This works fine. But then I decided I want the program to close if any of the three forms is closed. My first thought was to make it so that closing the other forms causes Form1 to close:


Private Sub Form2_FormClosed(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed
        SaveForm2Data()
        Form1.Close()
End Sub

Private Sub Form3_FormClosed(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed
        SaveForm3Data()
        Form1.Close()
End Sub

But then I get stuck in an infinite loop between the different form’s FormClosed events. Form1 closes Form2 closes Form1 closes Form2 . . . .

I suppose I could fix this by adding a variable to each form to tell if we’ve already called its FormClosed event once before. But is there a more elegant way? Should I perhaps be using Application.Exit() or End instead of Form1.Close?

Why does the program end without calling close on the other two forms? I.e. why is it aware of form1 but not 2 and 3?

Form1 is my startup Form (the first one opened).

In the Project Properties menu you can choose either to shut down when the startup form closes or when the last open form is closed. I have it set up to shut down when the startup form closes.

But what I really want is to make it shut down when any form closes, but only after calling the FormClosed events of all the other forms.

A better solution would be to simply call Application.Exit() in each form’s FormClosed handler (after saving your important data). Application.Exit() will signal all forms in the application to close as soon as they stop processing messages, and all of your remaining FormClosed handlers will be called.

(The FormClosed handler that calls Application.Exit() will not be called again, because Application.Exit only raises FormClosing (and subsequent FormClosed) events for all currently open forms, and since the form that just received a FormClosed event is no longer open, it won’t receive any further events.)

So, something like this:


Private Sub Form1_FormClosed(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed
        SaveForm1Data()
        Application.Exit()
End Sub

…And similarly for the other forms. Note: I’m only relatively certain that it will be written exactly as “Application.Exit()”; I have never programmed in Visual Basic, but I have been using .NET in C# for years, and most of this stuff is extremely consistent across the various .NET languages.

Thanks, Stealth Potato. That does seem to work better.

Interestingly, Microsoft’s documentation claims that Application.Exit() won’t raise the forms’ Closed events.

In fact, seeing that was what made me assume I should be using Form1.Close() instead of Application.Exit() in the first place.

But having just now tried it out, it seems that you’re right and the MSDN documentation is wrong.

Well, it all depends on what version of the .NET framework you’re targeting. The MSDN documentation page you’ve linked to is for one of the earlier versions, 1.1, and it’s correct as far as it goes. But you’re probably targeting 2.0 at least, if not 3.0 or 3.5. (It’s an option in your project settings.)

Note that your methods are handling the event System.Windows.Forms.Form.FormClosed, and not System.Windows.Forms.Form.Closed. It’s a very subtle difference, and probably the source of your initial confusion: the Closed event (and corresponding Closing event) was originally from .NET 1.1, and was made obsolete in .NET 2.0 with the introduction of the FormClosed and FormClosing events. They have the same purpose; Microsoft basically decided that FormClosed and FormClosing were better names, but kept the originals around as well for backwards-compatibility.

Application.Exit() in .NET 1.1 did not raise Closing or Closed events; it was necessary to do this manually. When .NET 2.0 came around, Application.Exit() was updated so that it did raise FormClosing and FormClosed events for all currently open forms. However, it still does not raise Closing or Closed events, since those are deprecated as of .NET 2.0.

So, long story short, Application.Exit() does raise Form.FormClosed, it doesn’t raise Form.Closed, and any confusion between the two is simply the result of Microsoft’s perennial hobgoblin of backwards compatibility. :stuck_out_tongue:

Ah, that explains it. Thanks again, Stealth Potato!

I reckon it’s more likely that Microsft realised that Application.Exit() should raise the Closing and Closed events, but by the time they realised this, there were already applications out there that depended on these events not being raised. Rather than change .NET to automatically raise these events, which could potentially break legacy apps, they decided to deprecate those events and introduce new ones with different names that are raised when they should be.

Not that this matters for the OP. I just enjoy speculating about the rationale behind certains decisions made by the .NET team.

Hmm. I suppose that is a more plausible explanation. “Form.FormClosed” does seem a bit redundant and out of line with the usual event naming conventions. An unfortunate choice, however you slice it; but then, fear of breaking legacy apps is a huge motivator of Microsoft’s API design choices. :o

Maybe so, but if that’s the case wouldn’t it have been easier and more sensible to just add a new method that fills the role of Application.Exit() while also raising Closing and Closed events?

They could have called it something like Application.CloseAll()

Well, you could go through the Application.OpenForms collection and call each one’s close method.