programmatically set win forms user controls to nothing on dispose - vb.net

I have vb.net winforms application. It has memory leaks which is caused by a third party control. I am left with the option of reducing instead of eliminating the memory leaks.
The form has several user controls declared as Friend WithEvents. They are not GCed when the form is closed, because the form is in memory and it holds a reference to the user control. However, when I set the user control references to nothing on dispose, the memory leak is reduced.
Now I want to expand this to the whole application instead of just one form. How would I use reflection to scan through all the user controls and set them to nothing on dispose ? Interesting to note is that the user controls are declared using "Friend WithEvents" signature. Is it possible to somehow use this while scanning ?

As #Plutonix states, if you dispose of the control, it will invalidate and dispose of its child controls. If you believe they are using dynamic controls, you could just recursively dispose of the controls?
Sub DisposeControls(parentControl as Control)
For each control as Control in parentControl.Controls
If control.HasChildren() Then Call Me.DisposeControls(control)
control.Dispose()
Next
End Sub
Edit: should mention, I am unsure if code above will compile - treat as pseudocode.

I ended up doing this. Its not optimal but it works really well. I have all the forms inheriting a base form. In the disposed method of that form, I use this reflection code.
Private Sub BaseForm_Disposed(sender As Object, e As EventArgs) Handles Me.Disposed
NullifyReferencesOnDispose()
RemoveHandler Me.Disposed, AddressOf BaseForm_Disposed
End Sub
Protected Overridable Sub NullifyReferencesOnDispose()
Dim typ = CType(Me.GetType, Reflection.TypeInfo)
For Each fld In typ.DeclaredFields
fld.SetValue(Me, Nothing)
Next
End Sub

Related

Built in control/function to clear text input when the form is close

I am creating multiple forms that have multiple textboxes in 1 form. I found that if I use Form1.Hide() the form only hide but the input didn't clear. So it will appear again if I open back.
When I search on the Internet, some of them suggest the hardcode method while some suggest using textbox.Clear()/textbox.text="". I tried Dispose() but it just clear the junk resources in the program but not the input.
So does Visual Basic has built-in code for this function as I think that this is a common function...
If you want to clear a TextBox then you should call its Clear method. If you want to do that when a form is hidden then you should handle the VisibleChanged event, test that the Visible property is False and then call the method. If you want to do this for every TextBox on the form then you need to loop through all the TextBoxes on the form in the VisibleChanged event handler and call their Clear method. Assuming all TextBoxes are directly on the form, that might look like this:
Private Sub Form1_VisibleChanged(sender As Object, e As EventArgs) Handles MyBase.VisibleChanged
If Not Visible Then
For Each tb In Controls.OfType(Of TextBox)
tb.Clear()
Next
End If
End Sub
If you want to do that in multiple forms then you can create one form with that functionality and then have the others inherit that class instead of the standard Form class.

When Microsoft Access is shutting down, how can I catch the current properties of a CustomTaskPane before the controls are disposed..?

I've created a VSTO addin for Microsoft Access by following the directions by Microsoft guru Andrew Whitechapel here, and it's working nicely. But the addin has a CustomTaskPane, and I'm having an issue with it when Access is closing.
If the CustomTaskPane is open when Access closes, the addin should save the properties of the CustomTaskPane controls. If code for this is placed in ThisAddIn_Shutdown(), I receive the following error:
System.ObjectDisposedException: Cannot access a disposed object.
at Microsoft.Office.Tools.CustomTaskPane.get_Control()
at MyAddin.ThisAddIn.ThisAddIn_Shutdown(Object sender, EventArgs e) in C:\...\ThisAddIn.vb:line nn
I'm not sure if this is the normal operation of CustomTaskPanes or Windows Forms controls, or if it's because VSTO isn't designed for Access. I'm wondering if it happens because Access doesn't have application-level events such as Access.Application."OnClose", as do the other VSTO-approved apps such as Excel & Word.
After some experimentation I found a workaround by using the HandleDestroyed event for the controls, which occurs before Dispose(), and thus the control properties are still available. This works:
Private Sub TextBox1_HandleDestroyed(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles TextBox1.HandleDestroyed
MsgBox(TextBox1.Text)
End Sub
Is there a better way..? Workarounds make me nervous.
In following the trail of events, I realized the answer to my own question. The focal point is the Dispose method in usercontrol.designer.vb. However, it is widely known designer-generated code shouldn't be directly modified, as it can and will be refreshed and over-written after any subsequent changes to the usercontrol in the designer.
Except...that rule doesn't completely apply to certain methods such as Dispose. See here. If the programmer subsequently moves such methods from usercontrol.designer.vb to usercontrol.vb, the designer will defer to them in usercontrol.vb and will not regenerate them in usercontrol.designer.vb.
And so, we've arrived at the answer: move the Dispose method to usercontrol.vb, remove the System.Diagnostics.DebuggerNonUserCode attribute, and then add the necessary code to save the control properties, as shown below:
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
Try
SaveUserControlProperties() <--- additional code added here
If disposing AndAlso components IsNot Nothing Then
components.Dispose()
End If
Finally
MyBase.Dispose(disposing)
End Try
End Sub

Where should controller-like code be written in my vb.net app?

I am refactoring an existing project that is one large file. All form controls live in a single form, and all event handlers live in the form's code behind.
I am refactoring out a piece of the UI into a User Control. One piece of this UI is an 'Add Contract' button. This goes to my data layer, adds a row to a DataTable, and various grids in other parts of form are updated with the new row.
My question is, where should I put the event handler for the button click in the User Control?
I could leave the event handler code in the parent form, and use AddHandler in the User Control to reference the parent form's sub
I could write all the event handler code in the User Control. In this case, I'd need to access the parent form to get a reference to the data layer.
I come from a web background where I'm used to the 'controller' handling this sort of thing. I suppose my parent form is turning into a kind of controller - but is this a good practice? Should I have a separate class that coordinates user interactions with my model layer, or, once I separate out all the UI into separate user controls, will the main form be a good place for this sort of thing?
Your user control should raise events that the form hosting the user control will handle. If the user control has a reference to the form, then the user control is not reusable in any other context; in other words, the user control is tightly coupled to that one form and form X would break if it tried to use the user control.
Note: If your form is the only thing that will use the user control, then there is not much re-usability value in making it a user control at all. Kind of similar to it is not much use to make an interface if there is only one implementation of said interface. That said from an organization standpoint, user controls can be used to clean up a crowded form. It also has the benefit of allowing for the dynamic loading of content in user controls.
There is nothing wrong with your parent form becoming the controller.
Here is a quick example (in this case the clicking of the button in the user control) of how you can raise events from your user control and subscribe to the event in the form:
Public Class UserControlClass
' Define event that will be raised by user control to anyone interested in handling the event
Public Event UC_Button1Click()
' Mechanism to allow event to be raised by user control
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
RaiseEvent UC_Button1Click()
End Sub
End Class
Now in your form class, you need to add a handler for the event raised by the user control, like this:
AddHandler userControl1.UC_Button1Click, AddressOf Button1_Click
Finally, you would create the method that is referenced in the AddressOf syntax, like this:
Public Sub Button1_Click(ByVal sender As Object, ByVal args As EventArgs)
' Do something here
End Sub

How to Close VB6 Parent Form From .NET Interop-ed UserControl?

I'm building a "plug-in" of sorts for an already-deployed VB6 executable. I'm using .NET along with COM-Interop. The VB6 creates a blank form and then loads my .NET UserControl into it (however by now the .dll has been compiled into a .ocx ActiveX UserControl that can be seen by VB6).
I've got it working well, but I would like to be able to close the VB6 parent form from inside of my .NET code. I am able to add VB6 code into my VB6-ifyed UserControl, but I cannot seem to find an event that fires when the UserControl is destroyed.
What I've tried so far:
Calling ParentForm.Close from the Disposing event of my .NET control. Receive error Object Reference not set to Instance of an Object.
Trying to close from the VB6 (I am able to get a handle on the parent form from there). Using ControlRemoved, Terminated, and a couple other hackish workarounds that truly make no sense in retrospect don't get triggered.
Calling Application.Exit (truly getting desperate at this point) closes the whole Application (who woulda thunk...)
I looked in the VB6 Interop code that I put in my .NET control and the following does look promising:
#Region "VB6 Events"
'This section shows some examples of exposing a UserControl's events to VB6. Typically, you just
'1) Declare the event as you want it to be shown in VB6
'2) Raise the event in the appropriate UserControl event.
Public Shadows Event Click() 'Event must be marked as Shadows since .NET UserControls have the same name.
Public Event DblClick()
Private Sub InteropUserControl_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Click
RaiseEvent Click()
End Sub
Private Sub InteropUserControl_DoubleClick(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.DoubleClick
RaiseEvent DblClick()
End Sub
#End Region
Is it just a matter of adding an event in this section? I'm not terribly familiar with Interop, or VB6 for that matter.
Alright, I figured it out and will post what I did for future generations :P
I was right with the event handlers in the VB6 code, and MarkJ was correct as well.
I created an event in the .NET code,
Public Event KillApp()
and then when I wanted to close everything, raised it:
RaiseEvent KillApp()
In the VB6 UserControl code, I declared the event again,
Public Event KillApp()
and then added a handler for it:
Private Sub MenuCtl_KillApp()
Unload Me.ParentForm
End Sub
where MenuCtl is my instance of the .NET control, and Me.ParentForm is the VB6 container form that houses the control. It now correctly closes the form!
In retrospect it makes a lot of sense, but I was unaware that you could pass events back and forth between managed/unmanaged that easily.

In VB/C# .NET, does a dialog always have to be disposed of manually?

I'm looking into disposing of resources and getting a little mixed up over the different ways to do it.
I've just found out that using Close() on a form shown with ShowDialog() only actually hides it and doesn't completely kill it off, so to speak. While this is useful for what I want at the moment, I'm now worrying about memory leaks elsewhere.
After using ShowDialog(), should I always call Dispose() on the form or use a Using block? Is there a difference? Or will the form be automatically disposed of when exiting the subroutine is was created in? For example, one of my typical simple usages:
Private Sub btnEdit_Click(sender As System.Object, e As System.EventArgs) Handles btnEdit.Click
Dim frm As New frmSomething()
frm.ShowDialog()
'frm is exited by clicking the X or using Close()
'At this point, frm is still in memory. Is it automatically disposed of
'after the End Sub here, or should I do frm.Dispose() ?
End Sub
It won't be automatically disposed, no. It may well not cause a problem, and there may well be a finalizer to do everything that's required, so the cost would just be some extra resources before the finalizer runs (and a longer delay before eventual GC) but it would be better to dispose it explicitly - ideally with a Using statement:
Using frm As New frmSomething()
frm.ShowDialog()
End Using