|
-
Nov 23rd, 2010, 10:04 AM
#1
Thread Starter
Member
[RESOLVED] Dynamically Adding/Removing Event Handler
Hello and thanks for looking.
The background:
*I have a TableGridLayout (6 x 6) which host 36 buttons created at design time.
*From the 36 buttons, only a handfull maybe needed at anyone time depending on data retrieved from an SQL database.
* Everytime I need to 'clear' the grid to populate with new items, I clear the text of each button, make it visable if required and then upadate with the new text.
*On every pass if a button becomes active, I add an event handler to the button like this.
e.g.
Code:
For Each Control In GrdTableLayoutPanel.Controls
Control.text = **Button Name from DB**
AddHandler Control.Click, AddressOf PopulateProducts
Control.Enabled = True
Next
The problem:
As the same 36 buttons are being used for different event handlers e.g. button1 used for getting products so has PopulateProducts sub but it can also be used to populate services so at some point I may be calling
Code:
AddHandler Control.Click, AddressOf Services
Now this is fine the first few times the button is clicked but after a while you can see that any old handles attached to the button click are also fired.
So my question is how to remove all previous dynamically created event handlers?
I've done quite a bit of reading but I still have not been able to make it work.
I've tried calling for RemoveHandler in my Clear sub but the old events are still fired?
Code:
RemoveHandler Control.Click, PopulateProducts
RemoveHandler Control.Click, Services
Also the reason I start of with all 36 buttons created is because I tried creating the buttons as and when needed but the overhead was to much and performance was severly affected.
Any help would be appreciated or a better way of approaching this problem.
Thanks
Please mark your thread as Resolved; it helps identify which posters still require assistance and keeps things nice & tidy 
Acknowledge a helpful post; keeps the community going 
-
Nov 23rd, 2010, 10:11 AM
#2
Re: Dynamically Adding/Removing Event Handler
Do you only want each event to be handled once? If so then put the RemoveHandler at the end of the event handler itself and remove it from the 'sender'.
-
Nov 23rd, 2010, 11:41 AM
#3
Re: Dynamically Adding/Removing Event Handler
You may want to write an Extension method to remove all event handlers from a control using system.reflection. Something like this (partly taken from stackoverflow.com, originally in C#. I converted it to VB.net and made some changes)
Code:
Imports System.Runtime.CompilerServices
Imports System.Reflection
Imports System.ComponentModel
Module Module1
<Extension()> _
Public Sub RemoveAllHandlers(ByVal ctrl As Control)
If ctrl IsNot Nothing Then
Dim ctrlType As Type = ctrl.GetType()
Dim propInfo As PropertyInfo = ctrlType.GetProperty("Events", BindingFlags.Instance Or BindingFlags.NonPublic)
Dim handlerList As EventHandlerList = CType(propInfo.GetValue(ctrl, Nothing), EventHandlerList)
Dim headInfo As FieldInfo = handlerList.GetType.GetField("head", BindingFlags.Instance Or BindingFlags.NonPublic)
Dim handlerDict As New Dictionary(Of Object, [Delegate]())
Dim head As Object = headInfo.GetValue(handlerList)
If head IsNot Nothing Then
Dim entry As Type = head.GetType()
Dim handlerFI As FieldInfo = entry.GetField("handler", BindingFlags.Instance Or BindingFlags.NonPublic)
Dim keyFI As FieldInfo = entry.GetField("key", BindingFlags.Instance Or BindingFlags.NonPublic)
Dim nextFI As FieldInfo = entry.GetField("next", BindingFlags.Instance Or BindingFlags.NonPublic)
HelpAddEntry(handlerDict, head, handlerFI, keyFI, nextFI)
For Each pair As KeyValuePair(Of Object, [Delegate]()) In handlerDict
For x As Integer = pair.Value.Length - 1 To 0 Step -1
handlerList.RemoveHandler(pair.Key, pair.Value(x))
Next
Next
End If
End If
End Sub
Private Sub HelpAddEntry(ByVal dict As Dictionary(Of Object, [Delegate]()), ByVal entry As Object, ByVal handlerFI As FieldInfo, ByVal keyFI As FieldInfo, ByVal nextFI As FieldInfo)
Dim del As [Delegate] = CType(handlerFI.GetValue(entry), [Delegate])
Dim key As Object = keyFI.GetValue(entry)
Dim nxt As Object = nextFI.GetValue(entry)
Dim listeners As [Delegate]() = del.GetInvocationList()
If listeners IsNot Nothing AndAlso listeners.Length > 0 Then
dict.Add(key, listeners)
End If
If nxt IsNot Nothing Then
HelpAddEntry(dict, nxt, handlerFI, keyFI, nextFI)
End If
End Sub
End Module
Let us have faith that right makes might, and in that faith, let us, to the end, dare to do our duty as we understand it.
- Abraham Lincoln -
-
Nov 23rd, 2010, 12:24 PM
#4
Re: Dynamically Adding/Removing Event Handler
It seems like the design you have should work, but it will fail under a variety of situations. The two that come most readily to my mind are these:
1) If the Clear sub is either not running, or calling RemoveHandler on the wrong control.
2) If you have added the handler more than once for the control.
What is happening with AddHandler is that you are telling the object, "When you raise this event, please call this sub." (actually, the code is probably not that polite). The object maintains an internal list of all subs that it is supposed to call when the event is raised. Therefore, if you call AddHandler multiple times, the object just adds the sub to the internal list that many times. RemoveHandler is telling the object to remove that sub from the list, but it will only do it once, so if the sub is on there multiple times it will still get called.
That seems like the most likely scenario.
My usual boring signature: Nothing
 
-
Nov 24th, 2010, 11:51 AM
#5
Thread Starter
Member
Re: Dynamically Adding/Removing Event Handler
jmc yes I only wanted the event fired once but because I added the handler to multiple buttons but only one button will be actually clicked, i need a way of clearing all the handlers to all the buttons.
shaggy I think your exactly right and it was 2) that was occuring.
E.g. Button 1-20 I add PopulateProducts handler. User selects Button1 which then clears the button text and now add's PopulateService handler.
The RemoveHandler code was working but I was only calling it the once thinking it would remove all instances of the handler.
All I have done now is in my clear method I call it for all the buttons on the grid evertime I move to re create the grid with new elements.
Code:
For i = 0 To 35
GrdTableLayoutPanel.Controls(i).Text = ""
GrdTableLayoutPanel.Controls(i).Enabled = False
RemoveHandler GrdTableLayoutPanel.Controls(i).Click, AddressOf PopulateProducts
RemoveHandler GrdTableLayoutPanel.Controls(i).Click, AddressOf PopulateServices
Next
For those having same problem also look at the event handler itself. When I was calling the RemoveHandler PopulateProducts, I was getting the following warning:
The 'AddressOf' expression has no effect in this context because the method argument to 'AddressOf' requires a relaxed conversion to the delegate type of the event. Assign the 'AddressOf' expression to a variable, and use the variable to add or remove the method as the handler.
Am still not very clear on this warning as am new to custom events but assigning the PopulateProducts event handler with 'sender' and 'e' did the trick. As suggested you can try to assign the event handler to a variable and use that but my current way works fine.
My new PopulateProducts is :
Code:
Private Sub PopulateProduct(ByVal sender As System.Object, ByVal e As System.EventArgs)
'some code in here
End Sub
stannav I tried your module but i received object reference errors due to null controls. I'll take a look at it again and see if it can be usefull in other instances.
Thanks guys for leading me to my fix.
Please mark your thread as Resolved; it helps identify which posters still require assistance and keeps things nice & tidy 
Acknowledge a helpful post; keeps the community going 
Tags for this Thread
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|