PC Review


Reply
Thread Tools Rate Thread

Bug or bad design in System.Windows.Form.Form

 
 
Stephan Zaubzer
Guest
Posts: n/a
 
      16th Jan 2007
Hi
While developing a Windows forms application I encountered what I
consider to be a bug or a design flaw:

Consider to have 3 Forms namely A B and C. B is owned by A and C is
owned by B. If we close A, all three forms should close which is exactly
the design described in the class library reference on msdn.
However, if the windows run in NON modal mode (i.e. not showed using
ShowDialog(IWin32Window), closing A does call the OnFormClosing on A and
on B but NOT on C. C gets closed without a way to check for unsaved
data, because the FormClosing event doesn't get raised.
If window B and C run in Modal mode (ShowDialog(IWin32Window)), C raises
the FormClosingEvent (from within the OnFormClosing Method), but setting
e.cancle = false will NOT prevent the window from closing, and even
worse, the OnFormClosing method will run AFTER the window has already
been disposed.

Possible Reason:
The invaluable resource .NET Reflector gave me some insight to the
Implementation of the System.Windows.Forms.Form class. When a window
closes, it calls OnFormClosing on all of its MdiChildren and Owned
Windows. If e.cancle does not evaluate to false after these calls, the
form gets Disposed. Dispose(bool disposing) calls Dispose on all owned
forms, and thus it works recursively. But OnFormClosing does NOT. So
when I call A.Close() it "asks" B to close, and if B does not set
e.cancle, A disposes itself which in turn disposes B and C. So C gets
Disposed without a prior call to OnFormClosing and no event rising.
Shouldn't the (private) WMClose method in System.Windows.Forms.Form call
some RECURSIVE method to check recursively for all owner relationships
if the owned forms (in all levels of the owner relationship) can close?

Possible workaround: (NOT tested yet)
override the OnFormClosing method that it calls OnFormClosing for all
owned forms recursively. This however will cause the OnFormClosing
method to be called twice for all owned forms that are directly owned by
the "root" form, because WMClose calls OnFormClosing on all its directly
owned forms, and later calls this.OnFormClosing.
The loop which calls OnFormClosing for all owned forms shouldn't be in
WMClose but rather in OnFormClosing itself, thus be recursive.
For MdiWindows this problem does not exist, because MdiCild Relations
can only exist in one level, so for MdiChildren this problem does not
exist. But when one closes the MdiParent of an MdiChild which owns some
window X, OnFormClosing and the FormClosing Event will NOT be called for X.

Has anyone ever encountered problems with this behavior as I did? And
how did you solve the problem?
I would consider this behavior either a bug or a design flaw!

Regards
Stephan
 
Reply With Quote
 
 
 
 
Richard Coltrane
Guest
Posts: n/a
 
      17th Jan 2007
Hi Stephan,

The current behaviour seems appropriate to me. Basically when B is closing
it should notify (i.e you code it too notify) any child forms it spawned
that this event is about to occur and so they can do a state save. "A"
shouldn't need to be aware of C; nor even consider C, as A has no contract
with C; only B has that.

If C is an options type dialog for B then i would spawn it modal so A cannot
be closed with first closing C. If C is spawned by B and can used
independantly of B thereafter then i would look at the Command/Observer
pattern or something to tie it back into the root form A.i.e create a
contract.

I definitely wouldn't think it a bug nor a design flaw; your example is
very specific and simply doesn't fall within the out of the box
behaviour;but you can easily make your forms do what you want with a little
bit of code. You simply need to register C with B yourself.

Richard


"Stephan Zaubzer" <(E-Mail Removed)> wrote in message
news:(E-Mail Removed)...
> Hi
> While developing a Windows forms application I encountered what I consider
> to be a bug or a design flaw:
>
> Consider to have 3 Forms namely A B and C. B is owned by A and C is owned
> by B. If we close A, all three forms should close which is exactly the
> design described in the class library reference on msdn.
> However, if the windows run in NON modal mode (i.e. not showed using
> ShowDialog(IWin32Window), closing A does call the OnFormClosing on A and
> on B but NOT on C. C gets closed without a way to check for unsaved data,
> because the FormClosing event doesn't get raised.
> If window B and C run in Modal mode (ShowDialog(IWin32Window)), C raises
> the FormClosingEvent (from within the OnFormClosing Method), but setting
> e.cancle = false will NOT prevent the window from closing, and even worse,
> the OnFormClosing method will run AFTER the window has already been
> disposed.
>
> Possible Reason:
> The invaluable resource .NET Reflector gave me some insight to the
> Implementation of the System.Windows.Forms.Form class. When a window
> closes, it calls OnFormClosing on all of its MdiChildren and Owned
> Windows. If e.cancle does not evaluate to false after these calls, the
> form gets Disposed. Dispose(bool disposing) calls Dispose on all owned
> forms, and thus it works recursively. But OnFormClosing does NOT. So when
> I call A.Close() it "asks" B to close, and if B does not set e.cancle, A
> disposes itself which in turn disposes B and C. So C gets Disposed without
> a prior call to OnFormClosing and no event rising. Shouldn't the (private)
> WMClose method in System.Windows.Forms.Form call some RECURSIVE method to
> check recursively for all owner relationships if the owned forms (in all
> levels of the owner relationship) can close?
>
> Possible workaround: (NOT tested yet)
> override the OnFormClosing method that it calls OnFormClosing for all
> owned forms recursively. This however will cause the OnFormClosing method
> to be called twice for all owned forms that are directly owned by the
> "root" form, because WMClose calls OnFormClosing on all its directly owned
> forms, and later calls this.OnFormClosing.
> The loop which calls OnFormClosing for all owned forms shouldn't be in
> WMClose but rather in OnFormClosing itself, thus be recursive.
> For MdiWindows this problem does not exist, because MdiCild Relations can
> only exist in one level, so for MdiChildren this problem does not exist.
> But when one closes the MdiParent of an MdiChild which owns some window X,
> OnFormClosing and the FormClosing Event will NOT be called for X.
>
> Has anyone ever encountered problems with this behavior as I did? And how
> did you solve the problem?
> I would consider this behavior either a bug or a design flaw!
>
> Regards
> Stephan



 
Reply With Quote
 
Bryan Phillips
Guest
Posts: n/a
 
      17th Jan 2007
I was able to reproduce the problem even using Show(IWin32Window), but
no idea how to gracefully close FormC when FormA closes.

--
Bryan Phillips
MCSD, MCDBA, MCSE
Blog: http://bphillips76.spaces.live.com



"Stephan Zaubzer" <(E-Mail Removed)> wrote in message
news:(E-Mail Removed):

> Hi
> While developing a Windows forms application I encountered what I
> consider to be a bug or a design flaw:
>
> Consider to have 3 Forms namely A B and C. B is owned by A and C is
> owned by B. If we close A, all three forms should close which is exactly
> the design described in the class library reference on msdn.
> However, if the windows run in NON modal mode (i.e. not showed using
> ShowDialog(IWin32Window), closing A does call the OnFormClosing on A and
> on B but NOT on C. C gets closed without a way to check for unsaved
> data, because the FormClosing event doesn't get raised.
> If window B and C run in Modal mode (ShowDialog(IWin32Window)), C raises
> the FormClosingEvent (from within the OnFormClosing Method), but setting
> e.cancle = false will NOT prevent the window from closing, and even
> worse, the OnFormClosing method will run AFTER the window has already
> been disposed.
>
> Possible Reason:
> The invaluable resource .NET Reflector gave me some insight to the
> Implementation of the System.Windows.Forms.Form class. When a window
> closes, it calls OnFormClosing on all of its MdiChildren and Owned
> Windows. If e.cancle does not evaluate to false after these calls, the
> form gets Disposed. Dispose(bool disposing) calls Dispose on all owned
> forms, and thus it works recursively. But OnFormClosing does NOT. So
> when I call A.Close() it "asks" B to close, and if B does not set
> e.cancle, A disposes itself which in turn disposes B and C. So C gets
> Disposed without a prior call to OnFormClosing and no event rising.
> Shouldn't the (private) WMClose method in System.Windows.Forms.Form call
> some RECURSIVE method to check recursively for all owner relationships
> if the owned forms (in all levels of the owner relationship) can close?
>
> Possible workaround: (NOT tested yet)
> override the OnFormClosing method that it calls OnFormClosing for all
> owned forms recursively. This however will cause the OnFormClosing
> method to be called twice for all owned forms that are directly owned by
> the "root" form, because WMClose calls OnFormClosing on all its directly
> owned forms, and later calls this.OnFormClosing.
> The loop which calls OnFormClosing for all owned forms shouldn't be in
> WMClose but rather in OnFormClosing itself, thus be recursive.
> For MdiWindows this problem does not exist, because MdiCild Relations
> can only exist in one level, so for MdiChildren this problem does not
> exist. But when one closes the MdiParent of an MdiChild which owns some
> window X, OnFormClosing and the FormClosing Event will NOT be called for X.
>
> Has anyone ever encountered problems with this behavior as I did? And
> how did you solve the problem?
> I would consider this behavior either a bug or a design flaw!
>
> Regards
> Stephan


 
Reply With Quote
 
RobinS
Guest
Posts: n/a
 
      17th Jan 2007

"Stephan Zaubzer" <(E-Mail Removed)> wrote in message
news:(E-Mail Removed)...
> Hi
> While developing a Windows forms application I encountered what I
> consider to be a bug or a design flaw:
>
> Consider to have 3 Forms namely A B and C. B is owned by A and C is
> owned by B. If we close A, all three forms should close which is
> exactly the design described in the class library reference on msdn.
> However, if the windows run in NON modal mode (i.e. not showed using
> ShowDialog(IWin32Window), closing A does call the OnFormClosing on A
> and on B but NOT on C. C gets closed without a way to check for
> unsaved data, because the FormClosing event doesn't get raised.
> If window B and C run in Modal mode (ShowDialog(IWin32Window)), C
> raises the FormClosingEvent (from within the OnFormClosing Method),
> but setting e.cancle = false will NOT prevent the window from closing,
> and even worse, the OnFormClosing method will run AFTER the window has
> already been disposed.
>
> Possible Reason:
> The invaluable resource .NET Reflector gave me some insight to the
> Implementation of the System.Windows.Forms.Form class. When a window
> closes, it calls OnFormClosing on all of its MdiChildren and Owned
> Windows. If e.cancle does not evaluate to false after these calls, the
> form gets Disposed. Dispose(bool disposing) calls Dispose on all owned
> forms, and thus it works recursively. But OnFormClosing does NOT. So
> when I call A.Close() it "asks" B to close, and if B does not set
> e.cancle, A disposes itself which in turn disposes B and C. So C gets
> Disposed without a prior call to OnFormClosing and no event rising.
> Shouldn't the (private) WMClose method in System.Windows.Forms.Form
> call some RECURSIVE method to check recursively for all owner
> relationships if the owned forms (in all levels of the owner
> relationship) can close?
>
> Possible workaround: (NOT tested yet)
> override the OnFormClosing method that it calls OnFormClosing for all
> owned forms recursively. This however will cause the OnFormClosing
> method to be called twice for all owned forms that are directly owned
> by the "root" form, because WMClose calls OnFormClosing on all its
> directly owned forms, and later calls this.OnFormClosing.
> The loop which calls OnFormClosing for all owned forms shouldn't be in
> WMClose but rather in OnFormClosing itself, thus be recursive.
> For MdiWindows this problem does not exist, because MdiCild Relations
> can only exist in one level, so for MdiChildren this problem does not
> exist. But when one closes the MdiParent of an MdiChild which owns
> some window X, OnFormClosing and the FormClosing Event will NOT be
> called for X.
>
> Has anyone ever encountered problems with this behavior as I did? And
> how did you solve the problem?
> I would consider this behavior either a bug or a design flaw!
>
> Regards
> Stephan


Do you want C closed every time you close B? Couldn't you put that
in the Form_Closing event of Form B?

Another idea is if you want to close all open forms when form A is
closed, you can do that. In VB2005, try this in the Form Closing
event of form A.

For Each myForm As Form In My.Application.OpenForms
If myForm.Name <> me.Name Then
myForm.Close()
End If
Next

Good luck.
Robin S.


 
Reply With Quote
 
Stephan Zaubzer
Guest
Posts: n/a
 
      17th Jan 2007
I am aware of the fact, that there are several ways to circumvent this
problem without having to write too much code. I know that having this
owner hierarchy represents a bad design in my own software. But what is
considered to be a bug and what is a feature?
I consider a bug to be some behavior that is not in accordance with the
official documentation.
Regarding OnFormClosing and the FormClosing Event MSDN says "Occurs
before the form is closed". Obviously this event does not raise always.
So either the documentation or the class library is not correct.

As you said, A does not need to be aware of C, and should not be aware
of C. Only B needs to be aware of C. And if B is allowed to own a form C
while being owned by another Form A, it also hast to notify C before it
closes. This would easily be possible by designing the WMClose and
OnFormClosing method in a form in a different way. If this was
impossible, an owned form should not be allowed to own another form.
Only these to options provide that the documentation is consistent with
the implementation. Or the documentation should note that a Form that is
owned in a level of 2 or higher does not rise the FormClosing event.
Regards
Stephan

Richard Coltrane schrieb:
> Hi Stephan,
>
> The current behaviour seems appropriate to me. Basically when B is closing
> it should notify (i.e you code it too notify) any child forms it spawned
> that this event is about to occur and so they can do a state save. "A"
> shouldn't need to be aware of C; nor even consider C, as A has no contract
> with C; only B has that.
>
> If C is an options type dialog for B then i would spawn it modal so A cannot
> be closed with first closing C. If C is spawned by B and can used
> independantly of B thereafter then i would look at the Command/Observer
> pattern or something to tie it back into the root form A.i.e create a
> contract.
>
> I definitely wouldn't think it a bug nor a design flaw; your example is
> very specific and simply doesn't fall within the out of the box
> behaviour;but you can easily make your forms do what you want with a little
> bit of code. You simply need to register C with B yourself.
>
> Richard
>
>
> "Stephan Zaubzer" <(E-Mail Removed)> wrote in message
> news:(E-Mail Removed)...
>> Hi
>> While developing a Windows forms application I encountered what I consider
>> to be a bug or a design flaw:
>>
>> Consider to have 3 Forms namely A B and C. B is owned by A and C is owned
>> by B. If we close A, all three forms should close which is exactly the
>> design described in the class library reference on msdn.
>> However, if the windows run in NON modal mode (i.e. not showed using
>> ShowDialog(IWin32Window), closing A does call the OnFormClosing on A and
>> on B but NOT on C. C gets closed without a way to check for unsaved data,
>> because the FormClosing event doesn't get raised.
>> If window B and C run in Modal mode (ShowDialog(IWin32Window)), C raises
>> the FormClosingEvent (from within the OnFormClosing Method), but setting
>> e.cancle = false will NOT prevent the window from closing, and even worse,
>> the OnFormClosing method will run AFTER the window has already been
>> disposed.
>>
>> Possible Reason:
>> The invaluable resource .NET Reflector gave me some insight to the
>> Implementation of the System.Windows.Forms.Form class. When a window
>> closes, it calls OnFormClosing on all of its MdiChildren and Owned
>> Windows. If e.cancle does not evaluate to false after these calls, the
>> form gets Disposed. Dispose(bool disposing) calls Dispose on all owned
>> forms, and thus it works recursively. But OnFormClosing does NOT. So when
>> I call A.Close() it "asks" B to close, and if B does not set e.cancle, A
>> disposes itself which in turn disposes B and C. So C gets Disposed without
>> a prior call to OnFormClosing and no event rising. Shouldn't the (private)
>> WMClose method in System.Windows.Forms.Form call some RECURSIVE method to
>> check recursively for all owner relationships if the owned forms (in all
>> levels of the owner relationship) can close?
>>
>> Possible workaround: (NOT tested yet)
>> override the OnFormClosing method that it calls OnFormClosing for all
>> owned forms recursively. This however will cause the OnFormClosing method
>> to be called twice for all owned forms that are directly owned by the
>> "root" form, because WMClose calls OnFormClosing on all its directly owned
>> forms, and later calls this.OnFormClosing.
>> The loop which calls OnFormClosing for all owned forms shouldn't be in
>> WMClose but rather in OnFormClosing itself, thus be recursive.
>> For MdiWindows this problem does not exist, because MdiCild Relations can
>> only exist in one level, so for MdiChildren this problem does not exist.
>> But when one closes the MdiParent of an MdiChild which owns some window X,
>> OnFormClosing and the FormClosing Event will NOT be called for X.
>>
>> Has anyone ever encountered problems with this behavior as I did? And how
>> did you solve the problem?
>> I would consider this behavior either a bug or a design flaw!
>>
>> Regards
>> Stephan

>
>

 
Reply With Quote
 
=?Utf-8?B?Q2lhcmFuIE8nJ0Rvbm5lbGw=?=
Guest
Posts: n/a
 
      18th Jan 2007
My only comment on you code it the comparing of the forms names is wasteful
and crude. This is better to ensure it isnt closing itself:
For Each myForm As Form In My.Application.OpenForms
If Not Object.Equals(myForm,me) Then
myForm.Close()
End If
Next
--
Ciaran O''Donnell
http://wannabedeveloper.spaces.live.com


"RobinS" wrote:

>
> "Stephan Zaubzer" <(E-Mail Removed)> wrote in message
> news:(E-Mail Removed)...
> > Hi
> > While developing a Windows forms application I encountered what I
> > consider to be a bug or a design flaw:
> >
> > Consider to have 3 Forms namely A B and C. B is owned by A and C is
> > owned by B. If we close A, all three forms should close which is
> > exactly the design described in the class library reference on msdn.
> > However, if the windows run in NON modal mode (i.e. not showed using
> > ShowDialog(IWin32Window), closing A does call the OnFormClosing on A
> > and on B but NOT on C. C gets closed without a way to check for
> > unsaved data, because the FormClosing event doesn't get raised.
> > If window B and C run in Modal mode (ShowDialog(IWin32Window)), C
> > raises the FormClosingEvent (from within the OnFormClosing Method),
> > but setting e.cancle = false will NOT prevent the window from closing,
> > and even worse, the OnFormClosing method will run AFTER the window has
> > already been disposed.
> >
> > Possible Reason:
> > The invaluable resource .NET Reflector gave me some insight to the
> > Implementation of the System.Windows.Forms.Form class. When a window
> > closes, it calls OnFormClosing on all of its MdiChildren and Owned
> > Windows. If e.cancle does not evaluate to false after these calls, the
> > form gets Disposed. Dispose(bool disposing) calls Dispose on all owned
> > forms, and thus it works recursively. But OnFormClosing does NOT. So
> > when I call A.Close() it "asks" B to close, and if B does not set
> > e.cancle, A disposes itself which in turn disposes B and C. So C gets
> > Disposed without a prior call to OnFormClosing and no event rising.
> > Shouldn't the (private) WMClose method in System.Windows.Forms.Form
> > call some RECURSIVE method to check recursively for all owner
> > relationships if the owned forms (in all levels of the owner
> > relationship) can close?
> >
> > Possible workaround: (NOT tested yet)
> > override the OnFormClosing method that it calls OnFormClosing for all
> > owned forms recursively. This however will cause the OnFormClosing
> > method to be called twice for all owned forms that are directly owned
> > by the "root" form, because WMClose calls OnFormClosing on all its
> > directly owned forms, and later calls this.OnFormClosing.
> > The loop which calls OnFormClosing for all owned forms shouldn't be in
> > WMClose but rather in OnFormClosing itself, thus be recursive.
> > For MdiWindows this problem does not exist, because MdiCild Relations
> > can only exist in one level, so for MdiChildren this problem does not
> > exist. But when one closes the MdiParent of an MdiChild which owns
> > some window X, OnFormClosing and the FormClosing Event will NOT be
> > called for X.
> >
> > Has anyone ever encountered problems with this behavior as I did? And
> > how did you solve the problem?
> > I would consider this behavior either a bug or a design flaw!
> >
> > Regards
> > Stephan

>
> Do you want C closed every time you close B? Couldn't you put that
> in the Form_Closing event of Form B?
>
> Another idea is if you want to close all open forms when form A is
> closed, you can do that. In VB2005, try this in the Form Closing
> event of form A.
>
> For Each myForm As Form In My.Application.OpenForms
> If myForm.Name <> me.Name Then
> myForm.Close()
> End If
> Next
>
> Good luck.
> Robin S.
>
>
>

 
Reply With Quote
 
 
 
Reply

Thread Tools
Rate This Thread
Rate This Thread:

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
How would you design this Windows form? Ronald S. Cook Microsoft VB .NET 5 23rd Mar 2007 12:46 PM
system.windows.form.datagrid control in vb6 form =?Utf-8?B?UmFqZXNoIEt1bWFyIENob3VkaGFyeQ==?= Microsoft C# .NET 1 10th Apr 2006 05:26 AM
Cannot open derived form in design view if the base form is marked 'abstract'. Yasutaka Ito Microsoft Dot NET 2 26th Jan 2004 04:37 PM
Cannot open derived form in design view if the base form is marked 'abstract'. Yasutaka Ito Microsoft Dot NET Framework Forms 3 26th Jan 2004 04:37 PM
Outlook 2003, "Design this form" opens Outlook 2000's default contact form design OSHM Microsoft Outlook Contacts 3 3rd Dec 2003 08:30 PM


Features
 

Advertising
 

Newsgroups
 


All times are GMT +1. The time now is 12:44 PM.