Hi Jay
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