Weird Problem: Closing MDI Parent re-directed to MDI Child

C

Charles Law

For some reason, when I click the X to close my MDI parent form, the action
appears to be re-directed to one of the MDI child forms, and the parent
remains open. I am then unable to close the application.

What should happen, is that the main MDI form should close, taking the child
forms with it. There is code to loop through the child forms, remove the
controls on each of them, and then close the form, but this code should
execute only when the user clicks a specific menu option.

What actually happens is when I click the X to close the main form, the
first of the child forms has all its controls removed, and nothing else
happens.

I realise that I haven't given much to go on, but I just wondered if anyone
had any thoughts/suggestions of what could cause this.

TIA

Charles
 
K

Ken Tucker [MVP]

Hi,

Sorry I haven't been able to reproduce that behavior. Is it possible
you have code in forms closing event preventing the app from closing
properly?

Ken
-----------------
 
C

Charles Law

Hi Ken

The exact procedure that causes this effect is

Start main app
Open MDI child form
Close MDI child form
Open MDI child form
Click X to close main app

The last step, in this scenario, now causes the currently open MDI child
form to clear its controls and nothing else. If I were to click the X after
the first open then all would be well.

I have an event handler in the main MDI parent form, with 'Handles
MyBase.Closed', but this is never called after following the steps above (it
is called if I click the X after the first open).

What could be going on?

Charles
 
J

Jay B. Harlow [MVP - Outlook]

Charles,
For some reason, when I click the X to close my MDI parent form, the action
appears to be re-directed to one of the MDI child forms,
I would expect when you close the MDI parent that it will be re-directed to
all of your MDI child forms! As the MDI parent cannot close until all the
MDI child's are closed.

In other words. MDIParent.Close automatically causes MDIChild.Close!
and the parent
remains open. I am then unable to close the application.
Are you canceling the first MDI Child's Form.Closing event?
What should happen, is that the main MDI form should close, taking the child
forms with it.
This is normal .NET behavior, unless you have an event, such as Closing,
canceling the operation.
There is code to loop through the child forms, remove the
controls on each of them, and then close the form, but this code should
execute only when the user clicks a specific menu option.
The Form.Closing & Form.Closed event occurs for MDI child forms whether they
are closed explicitly via code or implicitly via the MDI parent being
closed.
What actually happens is when I click the X to close the main form, the
first of the child forms has all its controls removed, and nothing else
happens.
Which event are you removing all the controls in?


Are you handling the Form.Closing & Form.Closed events or did you override
the Form.OnClosing & Form.OnClosed methods. If you overrode OnClosing, did
you remember to call the base methods?

Hope this helps
Jay
 
C

Charles Law

Hi Jay

I see now why it goes to the child forms first. I should have thought of
that ;-)

At the MDI form level I handle the Form.Closed event. At the child level, I
have overridden the OnClosing method, the first line of which is
MyBase.OnClosing(e). It is in the OnClosing override that I remove all the
controls from the form.

I have simplified the process for making it behave oddly:

Start MDI form app
Open child form
Close child form
Click X to close app (nothing happens)

whereas

Start MDI form app
Open child form
Click X to close app (closes as normal)

Opening the child form loads some user controls and adds event handlers to
an object on the main form, where methods of the user controls are the
target.

The OnClosing method looks like this

<code>
Protected Overrides Sub OnClosing(ByVal e As
System.ComponentModel.CancelEventArgs)

MyBase.OnClosing(e)

' When the form closes, remove all controls from the Controls list
and
' dispose them first
m_SelectedControls.Clear()

' First copy the controls to the selected list, to avoid
' modifying the later loop whilst it is executing
m_SelectedControls.AddRange(Controls)

For Each ctl As Control In m_SelectedControls
Controls.Remove(ctl)

ctl.Dispose()
Next ctl

End Sub
</code>

This removes the controls prior to the form closing.

Does any of this make it clearer?

Charles
 
J

Jay B. Harlow [MVP - Outlook]

Charles,
This removes the controls prior to the form closing.

Does any of this make it clearer?
What is not clear is why you are removing the controls the explicitly? The
Form class will implicitly remove the controls for you.

Does one of your controls or another handler of the Closing event cancel the
Closing event?

Is this VS.NET 2002 or VS.NET 2003?

As Ken suggested, I am not able to recreate this behavior, even with
explicitly removing the controls...

What type of collection is m_SelectedControls?

Hope this helps
Jay
 
C

Charles Law

Hi Jay

m_SelectedControls is an ArrayList, and I am using VS 2003.

I'm removing the controls explicitly because they are user controls that
sink events, and ultimately, I know that as part of this process I must
unhook the handlers from the events. This is part of problem 2 (of 3), that
if I close a child form that is servicing events, without unhooking the
handlers, then when the event is next raised it still calls the code in the
old handler, and if the object that was the target of the handler has been
disposed an exception is thrown to the effect that I cannot access a control
that has been disposed.
Does one of your controls or another handler of the Closing event cancel the
Closing event?

None of my controls or handlers attempt to cancel the Closing event.

With regard to reproducing the behaviour, I wonder if the fact that the
controls sink events is significant. If I do the same thing w/o adding
controls to the form then all is well.

Charles
 
J

Jay B. Harlow [MVP - Outlook]

Charles,
I'm removing the controls explicitly because they are user controls that
sink events, and ultimately, I know that as part of this process I must
unhook the handlers from the events.
I would not remove the controls explicitly to unhook the handlers, I would
simply assign Nothing to the UserControl's SelectedObject property, where
the SelectedObject property unhooks the existing handler. Where the
SelectedObject property is a property of the correct name & type for the
type of "domain object" the user control displays. For example if I have a
Person object and a PersonControl that displays a Person object. The
PersonControl would have a SelectedPerson property. Within the
SelectedPerson property I would remove any handlers of the existing Person
before assigning the handlers for the new object.
With regard to reproducing the behaviour, I wonder if the fact that the
controls sink events is significant. If I do the same thing w/o adding
controls to the form then all is well.
I don't have a good test setup right now to test if having a user control
handle an external object's events is messing up the Closing, I would be
surprised if it does.

Did I ask if there are any Exceptions being raised that is causing the
Closing Event to end unexpectedly?

Hope this helps
Jay
 
C

Charles Law

Jay

I'm not sure I follow you when you talk about a SelectedObject.

If I can translate your example into my own, let's say I have a Device
object and a ShowStuff object. The ShowStuff object displays information
about the Device object, but knows nothing of the Device object per se [as I
am writing this I think a light is coming on]. As such, the handlers are
attached outside the ShowStuff and Device objects, so the ShowStuff object
does not know what events it is servicing and cannot therefore call
RemoveHandler. I should clarify that I am not removing the objects manually
in order to detach the handlers, as I realise that this does not work; it
was just a precursor to implementing this process.

If I had a SelectedObject property, what would it contain that could remove
the handlers w/o knowing which events had been attached? This question is
actually the subject of another post: 'How to iterate through a list of
sender events and detach all receiver handlers when given only the sender
and receiver as Objects'.

As you say, though, the fact that the handlers are not being detached is
probably not the cause of the app not closing.

I have just traced through the app again, and there are two ways that the
child form can be created:

Case 1.
a. User clicks toolbar button to create new, blank child form
b. User drags usercontrol onto form from toolbox

Case 2.
a. User selects file from open file dialog
b. File is opened and child form is recreated from file (xml)

In case 1, I can close the child form and close the app as normal. In case
2, I can close the child form but the app won't close. However, I cannot see
that the two methods are doing anything substantially different. The same
utility functions for creating the form and instantiating the usercontrol
are used in both cases.

Did I ask if there are any Exceptions being raised that is causing the
Closing Event to end unexpectedly?

I don't think you did, but anyway, there aren't any exceptions thrown. I
have all exceptions set to break into the debugger when they are thrown.

Charles
 
J

Jay B. Harlow [MVP - Outlook]

Charles,
One word: Encapsulation!
The ShowStuff object displays information
about the Device object, but knows nothing of the Device object per se
If ShowStuff knows nothing about the Device object, how does it know what to
display??

I would expect at the very least that ShowStuff would know about DeviceBase
abstract base (mustinherit) class or the IDevice interface, then
polymorphically be able to display any type of Device (derived class) that
may be able to be given to it. Any events that ShowStuff needed to be
implemented would be part of DeviceBase or IDevice.
As such, the handlers are
attached outside the ShowStuff and Device objects, so the ShowStuff object
does not know what events it is servicing and cannot therefore call
RemoveHandler.
Yes I read your other question, Herfried gave you the field you need.
However! I would have recommended encapsulation and have ShowStuff be
responsible for adding & removing any handlers that it owns. If you need to
use the DoStuffEvent to remove the handlers you can use
Delegate.GetInvocationList to get all the handlers on a Delegate, then use a
For Each to remove each one.

Something like (untested):
For Each handler As [Delegate] In DoStuffEvent.GetInvocationList()
[Delegate].Remove(DoStuffEvent, handler)
Next

Again I would only use this as a last resort! As I find it a little
anti-encapsulation, in that the object handling the event might have cleanup
that needs to be done before it stops handling the event, and the above
routine will not give the object handling the event a chance to do this
clean up...

This is especially importing if you show the Device in multiple ShowStuff
user controls at one time, where the user can open & close windows at will,
on the same document...
I should clarify that I am not removing the objects manually
in order to detach the handlers, as I realise that this does not work; it
was just a precursor to implementing this process.
I am suggesting that you do!
Case 2.
a. User selects file from open file dialog
b. File is opened and child form is recreated from file (xml)

In case 1, I can close the child form and close the app as normal. In case
2, I can close the child form but the app won't close. However, I cannot see
that the two methods are doing anything substantially different. The same
utility functions for creating the form and instantiating the usercontrol
are used in both cases.
Sounds like something in your 2b logic. 2b or not 2b that is the question.

Can you describe or show how you are opening the file & creating the child
form?

Hope this helps
Jay




Charles Law said:
Jay

I'm not sure I follow you when you talk about a SelectedObject.

If I can translate your example into my own, let's say I have a Device
object and a ShowStuff object. The ShowStuff object displays information
about the Device object, but knows nothing of the Device object per se [as I
am writing this I think a light is coming on]. As such, the handlers are
attached outside the ShowStuff and Device objects, so the ShowStuff object
does not know what events it is servicing and cannot therefore call
RemoveHandler. I should clarify that I am not removing the objects manually
in order to detach the handlers, as I realise that this does not work; it
was just a precursor to implementing this process.

If I had a SelectedObject property, what would it contain that could remove
the handlers w/o knowing which events had been attached? This question is
actually the subject of another post: 'How to iterate through a list of
sender events and detach all receiver handlers when given only the sender
and receiver as Objects'.

As you say, though, the fact that the handlers are not being detached is
probably not the cause of the app not closing.

I have just traced through the app again, and there are two ways that the
child form can be created:

Case 1.
a. User clicks toolbar button to create new, blank child form
b. User drags usercontrol onto form from toolbox

Case 2.
a. User selects file from open file dialog
b. File is opened and child form is recreated from file (xml)

In case 1, I can close the child form and close the app as normal. In case
2, I can close the child form but the app won't close. However, I cannot see
that the two methods are doing anything substantially different. The same
utility functions for creating the form and instantiating the usercontrol
are used in both cases.

Did I ask if there are any Exceptions being raised that is causing the
Closing Event to end unexpectedly?

I don't think you did, but anyway, there aren't any exceptions thrown. I
have all exceptions set to break into the debugger when they are thrown.

Charles


Jay B. Harlow said:
Charles,
I would not remove the controls explicitly to unhook the handlers, I would
simply assign Nothing to the UserControl's SelectedObject property, where
the SelectedObject property unhooks the existing handler. Where the
SelectedObject property is a property of the correct name & type for the
type of "domain object" the user control displays. For example if I have a
Person object and a PersonControl that displays a Person object. The
PersonControl would have a SelectedPerson property. Within the
SelectedPerson property I would remove any handlers of the existing Person
before assigning the handlers for the new object.

I don't have a good test setup right now to test if having a user control
handle an external object's events is messing up the Closing, I would be
surprised if it does.

Did I ask if there are any Exceptions being raised that is causing the
Closing Event to end unexpectedly?

Hope this helps
Jay

in
the explicitly?
The thought form,
the until
all form,
the wondered
 
C

Charles Law

Hi Jay
One word: Encapsulation!

That was the little light. As I was describing the scenario I was thinking
'what I ought to have is an interface that defines the Device events that my
ShowStuff object would know about' :)

The file is opened as follows

<code>
Public Shared Function Read(ByVal filespec As String) As View

Dim fs As FileStream
Dim vw As View

' Creates an instance of the XmlSerializer class;
' specify the type of object to be deserialized
Try
Dim serializer As New XmlSerializer(GetType(View))

' A FileStream is needed to read the XML document
fs = New FileStream(filespec, FileMode.Open)

' Declare an object variable of the type to be deserialized
' Uses the Deserialize method to restore the object's state
with
' data from the XML document
Try
vw = CType(serializer.Deserialize(fs), View)

' View loaded, so update the filepsec
vw.m_Filespec = filespec

Catch ex As Exception
Throw New ConfigException("An error has occurred reading
configuration file " & filespec, ex)

Finally
If Not fs Is Nothing Then
fs.Close()
End If

End Try

Catch ex As FileNotFoundException
Return vw

Catch ex As Exception
Throw New ConfigException("An error has occurred reading
configuration file " & filespec, ex)

End Try

Return vw

End Function
</code>

Any existing forms are closed as follows

<code>
Private Sub CloseView()

' Unload any existing view
For Each frm As Form In MdiChildren
If TypeOf frm Is ItemGroupWindow Then
frm.Close()
End If
Next frm

' Update the current view to point to nothing
m_CurrentView = Nothing

UpdateViewStatus(Nothing)

End Sub
</code>

The forms are created as follows

<code>
For Each ig As ItemGroup In m_CurrentView.ItemGroups
If ig.IsOpen Then
Dim igw As ItemGroupWindow

' Create a new item group window, with the current form as
the MDI parent
igw = New ItemGroupWindow(Me)

With igw
' Set the properties of the new window
.StartPosition = FormStartPosition.Manual
.AutoScroll = True

.WindowState = ig.WindowState.ToFormWindowState
.Size = ig.WindowState.Size
.Location = ig.WindowState.Location
.Text = ig.Title

' Create a new item for each item definition in the item
group
For Each idef As ItemDefinition In ig.Items
Dim dev As DeviceBase

' Get a reference to the specific item from the
device list
dev = DeviceConfig.Devices(idef.Text)

.InstantiateDevice(dev, idef.Location)
Next idef

' Make the new window visible
.Show()

' Scroll the form to the saved position
.AutoScrollPosition = New
Point(-ig.ScrollPosition.X, -ig.ScrollPosition.Y)
End With
End If
Next ig
</code>

The method InstantiateDevice() creates the control defined by the parameters
and attaches handlers appropriate to the type of device created. It is used
whether the form is created manually or from the saved file. In fact, the
saved file is created by manually creating the forms and controls on-screen
and then clicking Save.

Charles


Jay B. Harlow said:
Charles,
One word: Encapsulation!
The ShowStuff object displays information
about the Device object, but knows nothing of the Device object per se
If ShowStuff knows nothing about the Device object, how does it know what to
display??

I would expect at the very least that ShowStuff would know about DeviceBase
abstract base (mustinherit) class or the IDevice interface, then
polymorphically be able to display any type of Device (derived class) that
may be able to be given to it. Any events that ShowStuff needed to be
implemented would be part of DeviceBase or IDevice.
As such, the handlers are
attached outside the ShowStuff and Device objects, so the ShowStuff object
does not know what events it is servicing and cannot therefore call
RemoveHandler.
Yes I read your other question, Herfried gave you the field you need.
However! I would have recommended encapsulation and have ShowStuff be
responsible for adding & removing any handlers that it owns. If you need to
use the DoStuffEvent to remove the handlers you can use
Delegate.GetInvocationList to get all the handlers on a Delegate, then use a
For Each to remove each one.

Something like (untested):
For Each handler As [Delegate] In DoStuffEvent.GetInvocationList()
[Delegate].Remove(DoStuffEvent, handler)
Next

Again I would only use this as a last resort! As I find it a little
anti-encapsulation, in that the object handling the event might have cleanup
that needs to be done before it stops handling the event, and the above
routine will not give the object handling the event a chance to do this
clean up...

This is especially importing if you show the Device in multiple ShowStuff
user controls at one time, where the user can open & close windows at will,
on the same document...
I should clarify that I am not removing the objects manually
in order to detach the handlers, as I realise that this does not work; it
was just a precursor to implementing this process.
I am suggesting that you do!
Case 2.
a. User selects file from open file dialog
b. File is opened and child form is recreated from file (xml)

In case 1, I can close the child form and close the app as normal. In case
2, I can close the child form but the app won't close. However, I cannot see
that the two methods are doing anything substantially different. The same
utility functions for creating the form and instantiating the usercontrol
are used in both cases.
Sounds like something in your 2b logic. 2b or not 2b that is the question.

Can you describe or show how you are opening the file & creating the child
form?

Hope this helps
Jay




Charles Law said:
Jay

I'm not sure I follow you when you talk about a SelectedObject.

If I can translate your example into my own, let's say I have a Device
object and a ShowStuff object. The ShowStuff object displays information
about the Device object, but knows nothing of the Device object per se
[as
I
am writing this I think a light is coming on]. As such, the handlers are
attached outside the ShowStuff and Device objects, so the ShowStuff object
does not know what events it is servicing and cannot therefore call
RemoveHandler. I should clarify that I am not removing the objects manually
in order to detach the handlers, as I realise that this does not work; it
was just a precursor to implementing this process.

If I had a SelectedObject property, what would it contain that could remove
the handlers w/o knowing which events had been attached? This question is
actually the subject of another post: 'How to iterate through a list of
sender events and detach all receiver handlers when given only the sender
and receiver as Objects'.

As you say, though, the fact that the handlers are not being detached is
probably not the cause of the app not closing.

I have just traced through the app again, and there are two ways that the
child form can be created:

Case 1.
a. User clicks toolbar button to create new, blank child form
b. User drags usercontrol onto form from toolbox

Case 2.
a. User selects file from open file dialog
b. File is opened and child form is recreated from file (xml)

In case 1, I can close the child form and close the app as normal. In case
2, I can close the child form but the app won't close. However, I cannot see
that the two methods are doing anything substantially different. The same
utility functions for creating the form and instantiating the usercontrol
are used in both cases.

Did I ask if there are any Exceptions being raised that is causing the
Closing Event to end unexpectedly?

I don't think you did, but anyway, there aren't any exceptions thrown. I
have all exceptions set to break into the debugger when they are thrown.

Charles


Charles,
I'm removing the controls explicitly because they are user controls that
sink events, and ultimately, I know that as part of this process I must
unhook the handlers from the events.
I would not remove the controls explicitly to unhook the handlers, I would
simply assign Nothing to the UserControl's SelectedObject property, where
the SelectedObject property unhooks the existing handler. Where the
SelectedObject property is a property of the correct name & type for the
type of "domain object" the user control displays. For example if I
have
a has
been remove
all are
the
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top