Cannot access disposed object .NET - vb.net

I have a windows form in .NET that will serve as a wizard to achieve something. This contains 3 steps: Step1, Step2, Step3.
Each step is again actually a user control. Main form contains a panel that display the current Step. When I change among steps then:
1) Dispose the current user control by calling its Dispose() method.
2) Clears the main form panel
3) Initialize the user control of next step and add it into the main panel
Now, the issue is, User control of step one contains one more user control. When I change to another step and come back to step 1, I get following error:
"Cannot access disposed object."
Because I have to first dispose the user control before actually displaying the another step. And when I come back to step 1 and tries to open the user control on step 1, it gives the aforementioned error.

Everything in the Controls collection of a Control is disposed when the control is disposed. So if you need to reuse those user controls, you'll need to remove them from the parent user control before disposing it.
You don't actually have to dispose the user control before displaying the next one. You can just remove it from the main form and put the new one on the main form when the step changes. Keep your user controls in a list or a dictionary and dispose them all when the main form is closing.

Why do you need to dispose the user control? Typically, when the form closes, it will dispose of all of it's child controls for you.
When you call dispose on an object, you are essentially telling it to go away.. you don't want it anymore. You can't change your mind -- once it's disposed, it's gone. Don't try to use it anymore.
Typically, you don't call dispose on an object directly; you should use the "using" pattern to avoid disposing of an object before you need it again, and to ensure the object is disposed once you are done with it.

Related

How can I stop an object in my application being disposed?

I have a user control with an ActiveX control on it. Due to the amount of time it takes to load the ActiveX control I thought what I can do is create an instance of the user control on application startup. Then when I wanted to use that user control I would just use the instance that I created. This works fine until I close a form where the instance was used. It disposes of it (as far as I can tell)
so when I try to use it again I can't. I wondered if it was possible to mark it in some way not to get disposed of on form close?
The exact message I get when trying to use it again is :
System.Runtime.InteropServices.InvalidComObjectException: 'COM object
that has been separated from its underlying RCW cannot be used.'
Or if there is another/better way to acheieve what I am trying to do?

How should you reload a form and have it re-initialize?

A older application loads some forms using implicit instances:
form2.showdialog()
Sometime between VS2008 32-bit and VS2013 64-bit, the forms stopped being initialized when they are reloaded. For example, if you load a form, close the form (using the Close method), and load the form again, the classes and controls (and, I assume, the form) are not initialized as new instances.
Re-initialization can be accomplished by putting me.dispose() in the FormClosed event, or by using an explicit instance of the form:
Using frm As New Form2
frm.ShowDialog()
End Using
Is there a good reason to use one of these methods over the other, or is there another method that should be used to cause a form to be initialized when it is reloaded?
Dispose will be called automatically if the form is shown using the Show method. If another method such as ShowDialog is used (your case it is), or the form is never shown at all, you must call Dispose yourself within your application. You can also handle the dispose by moving it from the designer file into the code file and handle things there as well.
On the other hand, Using statement typically makes your application safer to maintain and less prone to deadlocks and other misbehavior related to the lifecycle of the resource. I would stick by using this approach.
Also you cant put Me.Dispose in the Form Closed event (possible issues). If your using ShowDialog it will fail as it will dispose your objects first, if you need them they are gone.
Here's more on dispose: https://msdn.microsoft.com/en-us/library/aw58wzka(v=vs.110).aspx
The Form object and its child controls are not automatically disposed when you display the form with ShowDialog(). That sounds pretty quirky but this was done for a very good reason. After ShowDialog returns DialogResult.OK, you are normally going to obtain the dialog results. What nobody likes is that failing because of a ObjectDisposedException. Which would be likely to occur since the dialog results are often stored in controls.
You should always use the Using statement to ensure the form object and all of its controls are disposed.
A possible corner case is intentionally not disposing it because you like the redisplay the dialog with the original entered values. Which is not completely wrong, it is however a very expensive way to preserve those values. Those undisposed window objects cost an arm and a leg in system resources.
Pretty clear explanation from MSDN
Unlike non-modal forms, the Close method is not called by the .NET
Framework when the user clicks the close form button of a dialog box
or sets the value of the DialogResult property. Instead the form is
hidden and can be shown again without creating a new instance of the
dialog box. Because a form displayed as a dialog box is hidden instead
of closed, you must call the Dispose method of the form when the form
is no longer needed by your application.
When ShowDialog() called and closed, instance of the form will remain in the memory, and can be used again, for example get a result from some public property.
If you not using anymore this form, you need to call Dispose method to dispose form and form's controls
Dim myform As New MyDialogForm()
myform.ShowDialog()
Dim result As Object = myForm.SelectedResult()
myform.Dispose() 'need to call manually, if instance not used anymore
When you use Using keyword then Dispose method will be executed automatically at the end of the Using block
Dim result As Object
Using myform As New MyDialogForm()
myform.ShowDialog()
result = myForm.SelectedResult()
End Using 'myform.Dispose will be called
Bottom line: Both methods doing a same things.
But Using block will call Dispose method automatically
P.S. Putting Me.Dispose in the FormClosed eventhandler then
- instance of the form will stay in the memory even form was closed
- and will work only until you tried using disposed controls again. If you will try to show disposed object then ObjectDisposedException will be thrown.
If you not using form anymore then Using block will be best method

Cannot access disposed form despite creating it immediately prior

I have a windows form (VB/.NET) that I want to open when I click a particular button. It opens correctly the first time, but after closing it and trying to re-open it I get this error:
"ObjectDisposedException was unhandle: Cannot access a disposed object. Object name: frm8100VI"
Now, I create a new instance of the form right before calling it's show() method, so I don't know how it's disposed:
Dim form as New frm8100VI
form.show()
This code works correctly the first time it's called, but after closing the form once it will not work correctly again, saying the object has been disposed. To close the form all I do is call me.close()
I'm guessing you have your "Dim form as new frm8100VI" located in a space inside another object or in a public space where it never goes out of scope.
Once you have said "form.show", the user has worked with the form, closes the form.
At this point you cannot call "form.Show" again on the same form object (it is disposed).
You MUST reassign "form = New frm8011VI" again before you can call "form.show" (to re-iterate, after it has been disposed show fails)
If your trying to persist the information inside the "form" object then you should not actually close the form; in the form_unload event you show cancel the closing of the form and simply hide the form. If you do that you can call "form.Show" again without issue.

vb.net - does disposing of a form clean up all of its elements?

If I close a form in vb.net by using .dispose(), do I need to worry about cleaning up any of the elements that were created inside the form (like addHandler, etc) or does disposal clean up everything for me?
Thanks.
Closing a Form (or calling Dispose() on it) will clean up all of the components within that form. This means that any controls added to the form (or its controls, recursively) will automatically clean up.
Event handlers on controls within the form will get cleaned up.
That being said, this will not clean up event handlers which are subscribing to objects not owned by the form. If you used AddHandler to add an event handler to a type outside of the form, it would be a good practice to use RemoveHandler within an override of Form.Dispose(Boolean) to remove this subscription.

What is the correct way to fully remove a control from a parent control/form?

I have a UI element in my application where a Panel is used to host one of several potential custom UserControls. The Panel itself is hosted in a standardised UserControl that I am using something like a non-modal dialog that I'm calling a 'pane'.
The method I use is to instantiate a new instance of the standard pane, then with logic instantiate one of the several optional hosted controls inside it using Panel.Controls.Add(control). I then add the new pane to the interface control in a set location, again with a Control.Controls.Add(control), followed by a control.BringToFront() to maximise its z position.
This all works well, however when the time comes to hide the pane and destroy it, I cannot seem to fully get rid of it. Originally I was simply using Control.Controls.Remove(control) and for good measure setting the pane's Parent property to Nothing. This would have the desired effect of making the pane disappear, and my assumption was that now the control was unreferenced, that GC would dispose of it.
What I am seeing however is that the control still blits instantaneously onto the screen when the next outer hosting TabControl changes tab page, implying it still exists somewhere. I can confirm that this is not a graphical issue and the pane object persists using the VS Watch window's 'Make Object ID'. (At least I think this is proof, that without a code-accessible reference I can still directly see the object and its properties continue to exist.)
I have tried replacing
Control.Controls.Remove(pane)
pane.Parent = Nothing
with
pane.Dispose()
GC.Collect()
where the Dispose call I can confirm both removes the control from its parent's Controls collection and sets its Parent property to Nothing, but appears to do no more. It persists after forced GC and still blits onscreen occasionally.
This all leads to my original question, what is the proper way to remove and fully destroy controls after they have served their purpose?
According to this article from MSDN it seems like you might be experiencing side affects from the object being on the finalization queue.
A Dispose method should call the GC.SuppressFinalize method for the object it is disposing. If the object is currently on the finalization queue, GC.SuppressFinalize prevents its Finalize method from being called.
Translation: The finalize method isn't being called, and so the resources associated with your control are not being released. After a bit more digging, I found that you should
Always call Dispose before you release your last reference to the Component. Otherwise, the resources it is using will not be freed until the garbage collector calls the Component object's Finalize method.
From this article.
So either you need to release your last reference OR you need to call the components finalize method directly so your GC.Collect() will work.