Results 1 to 5 of 5

Thread: [RESOLVED] Dynamically Adding/Removing Event Handler

  1. #1

    Thread Starter
    Member Cyrax.NET's Avatar
    Join Date
    Feb 2007
    Posts
    54

    Resolved [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

  2. #2
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    111,221

    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'.
    Why is my data not saved to my database? | MSDN Data Walkthroughs
    VBForums Database Development FAQ
    My CodeBank Submissions: VB | C#
    My Blog: Data Among Multiple Forms (3 parts)
    Beginner Tutorials: VB | C# | SQL

  3. #3
    PowerPoster stanav's Avatar
    Join Date
    Jul 2006
    Location
    Providence, RI - USA
    Posts
    9,290

    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 -

  4. #4
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    40,102

    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

  5. #5

    Thread Starter
    Member Cyrax.NET's Avatar
    Join Date
    Feb 2007
    Posts
    54

    Resolved 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
  •  



Click Here to Expand Forum to Full Width