According to the OpenEdge documentation (documentation.progress.com/.../index.html and StackOverflow/Microsoft (stackoverflow.com/.../3097383, a Form that is not shown modally, gets disposed on Close(). When the Form is shown modally (i.e. ShowDialog()), the Dispose does not happen automatically and you need to call Dispose() yourself.
Progress thought it would be helpful to provide a way to by default have the Form dispose when it's closed as Dialog as well.
While this seems reasonable, there is a bug in the implementation:
In the Microsoft implementation, when closing a non-modal Form, the Dispose happens AFTER the form has closed (i.e. after OnFormClosed())
In the OpenEdge implementation of DisposeDialogOnClose, when closing a modal Form, the Dispose happens INSIDE the OnFormClosed() method of Progress.Windows.Form.
This can be seen by looking at the stack trace when overridding OnFormClosed and subscibing the Disposed event of a Form and closing the screen.
This causes code that uses the FormClosed event of the Form to no longer be able to access properties of the Form when started as dialog, while still being available when started non-modally!
In my particular case I'm saving some screen settings in FormClosed, and this fails when the same Form is shown as dialog, since the form is already disposed...
I'm trying a workaround now by setting DisposeDialogOnClose to false, and calling the Dispose() method myself at the very end of OnFormClosed(), but that will still cause problems for classes inheriting from my base form class...
You may be able to work around this by subscribing to the FormClosing event and saving the screen settings in that handler. FormClosing should be generated before FormClosed disposes the form.
Hi Matt,
Yes, FormClosing is an option, but that can be cancelled, and I don't want to save anything when that happens.
Saving preferences was just an example though, a subscriber to the FormClosed event should not need to worry that the sender is already disposed/disposing when started as Dialog...
I have currently worked around the problem like this:
METHOD OVERRIDE PROTECTED VOID OnFormClosed(e AS System.Windows.Forms.FormClosedEventArgs): DEFINE VARIABLE disposeOnClose AS LOGICAL NO-UNDO. IF Modal AND DisposeDialogOnClose THEN ASSIGN disposeOnClose = TRUE DisposeDialogOnClose = FALSE. /* snip... */ SUPER:OnFormClosed(e). /* snip... */ IF disposeOnClose THEN Dispose(). END METHOD.
but that doesn't prevent inherited classes to create a problematic scenario...
I could make the OnFormClosed FINAL and have a BeforeFormClosed and AfterFormClosed method that are overridable:
METHOD OVERRIDE PROTECTED FINAL VOID OnFormClosed(e AS System.Windows.Forms.FormClosedEventArgs): DEFINE VARIABLE disposeOnClose AS LOGICAL NO-UNDO. IF Modal AND DisposeDialogOnClose THEN ASSIGN disposeOnClose = TRUE DisposeDialogOnClose = FALSE. BeforeFormClosed(e). SUPER:OnFormClosed(e). AfterFormClosed(e). IF disposeOnClose THEN Dispose(). END METHOD.
but that's not making things less complicated...
Regarding the statement that "FormClosing is an option, but that can be cancelled" - yes, but it would be cancelled by the application itself. .NET is not going to cancel it. So they have total control over whether the form gets closed or not. I agree with Matt that they should have their code in the FormClosing event.
Why don't you perform your activities before the SUPER:OnFormClosed in the OnFormClosed override? That would be called before OpenEdge executes the Dispose() methods.
"but that will still cause problems for classes inheriting from my base form class..."
I see that point. But sometimes it just requires coding conventions.
[quote user="Mike Fechner"]
Why don't you perform your activities before the SUPER:OnFormClosed in the OnFormClosed override? That would be called before OpenEdge executes the Dispose() methods.
[/quote]
I have a UserControl subscribing to the FormClosed of its ParentForm, so there is no such fine granularity there (in the FormClosed event handler the ParentForm is already disposed).
While there are a number of more or less usable workarounds, I still think this is a bug that should at least get documented since this is unexpected behaviour...
That's a matter of interpretation :-)
But when Progress is tied to the limits allowed by the .NET WinForms framework, Progress might just not be able to fix what you call a bug.
Sure Mike, I also think Progress can't fix this (might have to take a look at the .NET framework reference source to be sure: referencesource.microsoft.com/)
Still, at least this forum thread can help people bumping into this...