How to Dispose a User Control and Remove Handlers

C

Charles Law

I have a form on which user controls are placed at runtime. When a control
is added to the form a handler is added for an event that a high-level
object raises, which must be handled by the new control. When I close the
form I am expecting that the control ceases to be.

However, if my object raises the event after the form has been closed, I
find that there is still a user control object that handles it. Clearly the
user control has not ceased to be after all.

What is the technique for destroying the user control so that it is no
longer around to handle my object's events?

I have tried calling Dispose on the user control but this does not help. I
do not want to have to remove the event handler manually (in code) unless
there is an easy way to remove all handlers in one fell swoop. In reality, I
have many different controls, any or all of which could be added to the form
at runtime, and which can individually handle a large number of events.
Coding to remove all the events manually would be a maintenance nightmare.

TIA

Charles
 
C

Cor Ligthert

Hi Charles,

I have learned a new English word.

Although your problems are always very deep, I was thinking that maybe this
could help for you.
(Take a look twice, it is not that simple as in first look). The usercontrol
I used is just a usercontrol with a textbox on it, nothing more.

It are dynamicly created usercontrols which acts in my idea completly in the
normal way. (I give you all the code to show you that it is a normal
situation, which uses the normal behaviour of a form). I did not test if it
does cease everything, that is yours to do, however I thought it meets a lot
of the needs you wrote.

I hope it helps?

Cor

\\\
Public Class Form1
Inherits System.Windows.Forms.Form
Public Sub New()
MyBase.New()
End Sub
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing Then
If Not (components Is Nothing) Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub
Private components As System.ComponentModel.IContainer
Friend UserControlA(0) As WindowsApplication1.UserControl1
Private Sub Form1_Load(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
Me.SuspendLayout()
ReDim UserControlA(UserControlA.Length)
Dim Cease As Integer = UserControlA.Length - 1
CreateNextControl(Cease)
ReDim UserControlA(UserControlA.Length)
Cease = UserControlA.Length - 1
CreateNextControl(Cease)
Me.ClientSize = New System.Drawing.Size(500, 500)
Me.ResumeLayout(False)
End Sub
Private Sub CreateNextControl(ByVal Cease As Integer)
Me.UserControlA(Cease) = New WindowsApplication10.UserControl1
Me.UserControlA(Cease).Location = New System.Drawing.Point _
((Cease - 1) * 250 + 1, 1)
Me.UserControlA(Cease).Size = New System.Drawing.Size(250, 250)
Me.UserControlA(Cease).TabIndex = Cease
AddHandler UserControlA(Cease).TextBox1.Click, _
AddressOf UserControlA_Click
Me.Controls.Add(Me.UserControlA(Cease))
End Sub
Private Sub UserControlA_Click(ByVal sender As _
System.Object, ByVal e As System.EventArgs)
For Each ctr As Control In Me.Controls
If TypeOf ctr Is UserControl1 Then
DirectCast(ctr, UserControl1).TextBox1.Text = ""
End If
Next
DirectCast(sender, TextBox).Text = "I am here"
End Sub
End Class
///
 
C

Charles Law

Hi Cor

Please excuse me being a bit slow on the uptake, but although I can run the
code you posted, I am not sure how it helps me. I have looked twice, as
instructed, but I don't see how it relates to the problem I have of a user
control living on after the form on which it resides is closed.

Am I missing something blindingly obvious?

Charles
[What is the new English word?]
 
C

Cor Ligthert

Hi Charles,

Because I created in the form that usercontrol completly as a normal control
and that it is used as a normal control (except that it is created
dynamicly, however not in the procedure but outside as a normal control) , I
would expect that it would dispose as a normal control. Did you make that
textbox usercontrol with it?

(the word is cease)

I will search further however I do not know why the usercontrol should in
this sitiation live on.

Cor

"
 
C

Charles Law

Hi Cor

Yes, I created the UserControl1 with a textbox on it.

I wonder if the user control survives because the object that has the
handler attached is outside the form. In that respect, the object still
holds a reference to the user control, albeit just one of its methods as
sink for the event. That is why I tried explicitly disposing the control,
but to no avail.

I really need a way to totally destroy the control, and for it to happen
immediately, but that does not seem to be the way of .NET.

Charles
 
C

Cor Ligthert

Hi Charles,

Is this something you want, (I use that click event with a "d" in the
textbox to remove the usercontrol have a look for that, just dirthy) (The
control is in the code now, you can paste it in a empty form/vb and it
should run).

Cor

Public Class Form1
Inherits System.Windows.Forms.Form
Public Sub New()
MyBase.New()
End Sub
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing Then
If Not (components Is Nothing) Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub
Private components As System.ComponentModel.IContainer
Friend UserControlA(0) As WindowsApplication10.UserControl1
Private Sub Form1_Load(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
Me.SuspendLayout()
ReDim UserControlA(UserControlA.Length)
Dim Cease As Integer = UserControlA.Length - 1
CreateNextControl(Cease)
ReDim UserControlA(UserControlA.Length)
Cease = UserControlA.Length - 1
CreateNextControl(Cease)
Me.ClientSize = New System.Drawing.Size(500, 500)
Me.ResumeLayout(False)
End Sub
Private Sub CreateNextControl(ByVal Cease As Integer)
Me.UserControlA(Cease) = New WindowsApplication10.UserControl1
Me.UserControlA(Cease).Location = New System.Drawing.Point _
((Cease - 1) * 250 + 1, 1)
Me.UserControlA(Cease).Size = New System.Drawing.Size(250, 250)
Me.UserControlA(Cease).TabIndex = Cease
AddHandler UserControlA(Cease).Click, _
AddressOf UserControlA_Click
Me.Controls.Add(Me.UserControlA(Cease))
End Sub
Private Sub UserControlA_Click(ByVal sender As _
System.Object, ByVal e As System.EventArgs)
If DirectCast(sender, TextBox).Text <> "" Then
If DirectCast(sender, TextBox).Text.Substring(0, 1).ToLower =
"d" Then
Me.Controls.Remove(DirectCast(sender, Control).Parent)
DirectCast(sender, Control).Parent.Dispose()
End If
End If
For Each ctr As Control In Me.Controls
If TypeOf ctr Is UserControl1 Then
DirectCast(ctr, UserControl1).TextBox1.Text = ""
End If
Next
DirectCast(sender, TextBox).Text = "I am here"
End Sub
End Class
Public Class UserControl1
Inherits System.Windows.Forms.UserControl
Public Shadows Event Click(ByVal sender As Object, _
ByVal e As System.EventArgs)
Public Sub New()
MyBase.New()
InitializeComponent()
End Sub
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing Then
If Not (components Is Nothing) Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub
Private components As System.ComponentModel.IContainer
Friend WithEvents TextBox1 As System.Windows.Forms.TextBox
Private Sub InitializeComponent()
Me.TextBox1 = New System.Windows.Forms.TextBox
Me.SuspendLayout()
Me.TextBox1.Location = New System.Drawing.Point(0, 0)
Me.TextBox1.Multiline = True
Me.TextBox1.Size = New System.Drawing.Size(100, 100)
Me.TextBox1.TabIndex = 0
Me.TextBox1.Text = ""
Me.Controls.Add(Me.TextBox1)
Me.Name = "UserControl1"
Me.Size = New System.Drawing.Size(101, 101)
Me.ResumeLayout(False)
End Sub
Private Sub TextBox1_Click(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles TextBox1.Click
RaiseEvent Click(sender, e)
End Sub
End Class
 
C

Charles Law

Hi Cor

I have modified the code to illustrate the problem. You can likewise paste
it into a forms app and run it. Mine happens to be called
WindowsApplication7.

<code>
Option Explicit On
Option Strict On

Public Class Form1

Inherits System.Windows.Forms.Form

Private components As System.ComponentModel.IContainer
Friend WithEvents Button1 As System.Windows.Forms.Button
Friend WithEvents Button2 As System.Windows.Forms.Button
Friend UserControlA As WindowsApplication7.UserControl1

Public Sub New()
MyBase.New()

InitializeComponent()

End Sub
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing Then
If Not (components Is Nothing) Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub
Private Sub InitializeComponent()
Me.Button1 = New System.Windows.Forms.Button
Me.Button2 = New System.Windows.Forms.Button
Me.UserControlA = New WindowsApplication7.UserControl1
Me.SuspendLayout()
'
'Button1
'
Me.Button1.Location = New System.Drawing.Point(240, 16)
Me.Button1.Name = "Button1"
Me.Button1.TabIndex = 0
Me.Button1.Text = "Remove"
'
'Button2
'
Me.Button2.Location = New System.Drawing.Point(240, 56)
Me.Button2.Name = "Button2"
Me.Button2.TabIndex = 1
Me.Button2.Text = "Raise Event"
'
' UserControlA
Me.UserControlA.Location = New Point(1, 1)
Me.UserControlA.Size = New System.Drawing.Size(250, 250)
Me.Name = "UserControlA"
'
'Form1
'
Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
Me.ClientSize = New System.Drawing.Size(320, 237)
Me.Controls.Add(Me.Button2)
Me.Controls.Add(Me.Button1)
Me.Controls.Add(Me.UserControlA)
Me.Name = "Form1"
Me.ResumeLayout(False)

End Sub

Public Event MyEvent(ByVal sender As Object, ByVal e As EventArgs)

Private Sub Form1_Load(ByVal sender As Object, ByVal e As
System.EventArgs) Handles MyBase.Load

Me.ClientSize = New System.Drawing.Size(500, 500)

AddHandler MyEvent, AddressOf UserControlA.UserControl_EventHandler

End Sub

Private Sub OnEvent()

RaiseEvent MyEvent(Me, New EventArgs)

End Sub

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Button1.Click

Controls.Remove(UserControlA)

UserControlA.Dispose()

End Sub

Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Button2.Click

OnEvent()

End Sub

End Class

Public Class UserControl1

Inherits System.Windows.Forms.UserControl

Public Sub New()
MyBase.New()
InitializeComponent()
End Sub
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing Then
If Not (components Is Nothing) Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub

Private components As System.ComponentModel.IContainer
Friend WithEvents TextBox1 As System.Windows.Forms.TextBox

Private Sub InitializeComponent()
Me.TextBox1 = New System.Windows.Forms.TextBox
Me.SuspendLayout()
Me.TextBox1.Location = New System.Drawing.Point(0, 0)
Me.TextBox1.Multiline = True
Me.TextBox1.Size = New System.Drawing.Size(100, 100)
Me.TextBox1.TabIndex = 0
Me.TextBox1.Text = ""
Me.Controls.Add(Me.TextBox1)
Me.Name = "UserControl1"
Me.Size = New System.Drawing.Size(101, 101)
Me.ResumeLayout(False)
End Sub

Public Sub UserControl_EventHandler(ByVal sender As Object, ByVal e As
EventArgs)

MessageBox.Show("I am here")

End Sub

End Class
</code>

When the form is created two buttons and a user control are created. When
the form loads an even handler is added for the event MyEvent in Form1. The
handler is in the user control.

Click the button "Raise Event" and you will see a message box. Each time it
is clicked that is what happens.

Now, click the "Remove" button and the user control will disappear. It also
gets disposed.

Click the "Raise Event" button again, and the message box still appears,
even though the user control has gone. What I am trying to achieve is that
when the user control is destroyed, it no longer lives on to service events.

Thanks.

Charles
 
C

Cor Ligthert

Hi Charles,

I find this very clear, in class Form1 you have set a reference for an
adress in Usercontrol1.
For me that address is clearly from Form1, there is no reason why
UserControl1 should no about that address.

That reference is not cleared before you try to delete the Usercontrol1, so
while trying to dispose the reference is still there, very nice that it
stays, what can happen when it was deleted. Normaly just an error.

However why should disposing remove a reference in class Form1, because that
is your problem in my opinon.

When you add a removeHandler MyEvent works fine, there is even no need to
dispose the usercontrol.

However what am I seeing wrong?

Cor
 
C

Cor Ligthert

I find this very clear, in class Form1 you have set a reference for an
adress in Usercontrol1.
For me that address is clearly from Form1, there is no reason why
UserControl1 should no about that address.

know
 
C

Charles Law

Hi Cor

I see what you mean, but I was hoping that the handlers would be removed
automatically when I dispose the control. What I want to avoid is having to
write code that removes all the handlers manually.

In practice, I have many controls, and they all sink different events. When
I destroy a given user control, it could be one of many different controls,
and I would need a big Select Case block to pick the right type of control,
and then a whole stream of RemoveHandler statements to remove the handlers.
Furthermore, I don't necessarily know to which object the user control is
attached, so that would make it even harder to remove the handlers.

For example, at runtime I might have

AddHandler obj1.SomeEvent, AddressOf usercontrolA.SomeEventHandler
AddHandler obj1.SomeOtherEvent, AddressOf usercontrolB.SomeOtherEventHandler
AddHandler obj2.YetAnotherEvent, AddressOf
usercontrolA.YetAnotherEventHandler

So, an object can have event handlers in many user controls, and a user
control can handle events from many objects.

When I come to destroy usercontrolA, for example, I will have to remove the
handlers, but I won't know which object(s) it is handling events for. Do you
see my dilemma?

Any ideas?

Charles
 

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