Question How can I AddHandler to a New control, using AddressOf a passed control?

parkerea

New member
Joined
Jan 31, 2013
Messages
4
Location
Southern California
Programming Experience
10+
I am trying to replicate old VB6 Control Arrays, following this article:
Creating Control Arrays in Visual Basic .NET and Visual C# .NET
Creating Control Arrays in Visual Basic .NET and Visual C# .NET

My AddNew function (stripped down version below) includes a "model" control that should be replicated, including linking up the event handler. My problem is the AddHandler...AddressOf only seems to work with an actual control, not a control passed into a function.
Public Function AddNewButton(ByVal model As Button) As System.Windows.Forms.Button
' Create a new instance of the Button class
Dim aButton As New System.Windows.Forms.Button()
' Set properties as desired
aButton.Location = model.Location
aButton.Size = model.Size
... etc., etc.,
' Next line errors at design time: "AddressOf operand must be the name of a method (without parentheses)"
AddHandler aButton.Click, AddressOf model.Click
Return aButton
End Function

I don't have much experience with AddHandler or AddressOf, and none with Delegates. Can anyone with more experience help?

Thanks in advance,
parkerea
 
You simply write a method to handle the event, exactly as you would if the control was added at design time, and specify the address of that method. The only difference with the event handler is that it won't have a Handles clause.

The simplest way to create an event handler is the usual way, i.e. add a Button to your form in the designer and double-click it to generate a Click event handler. You can then delete the Button and the method will be left behind, minus the Handles clause. You can then rename the method as appropriate and use it in an AddHandler statement.

Don't forget that, any time you use AddHandler to add a handler to an event, you must use a corresponding RemoveHandler to remove the handler when you're done with the object. You should also be disposing controls created at run time because, unlike those added at design time, they will not be disposed automatically by the form.
 
You simply write a method to handle the event, exactly as you would if the control was added at design time, and specify the address of that method...

If I understand your solution, the new button control will have its own handler -- am I correct? However, I want the new button to share the model button's handler, like the "good old days" of VB6 Control Arrays. When the model button's event handler is called, I will include code to check which button was actually pushed (akin to checking the Index in VB6) and take appropriate action. Unless I misunderstand your solution, it won't work for my application.

BTW, I am converting an old VB6 app to VB 2008. The VB6 app is heavily dependent on control arrays, creating controls at run time. For each control array, the Index 0 was built at design time and is hidden. At run time, new controls are added to the control arrays using the Index 0 as a model. As new controls are added at run time, selected properties are updated, then the control is made visible. For the conversion to VB 2008, I seem to be almost there other than dealing with event handlers.

Although the example above is a button, my final solution will cover multiple types of controls and multiple events.


Thanks again,
parkerea
 
AddHandler works like this:
AddHandler object.eventname, AddressOf methodname

I will include code to check which button was actually pushed
Event 'sender' parameter identifies the object that raised the event.
 
AddHandler works like this:
AddHandler object.eventname, AddressOf methodname


Event 'sender' parameter identifies the object that raised the event.

Thanks for your input, but I don't need help on identifing the object that raised the event -- I'm fine on that.

I also know the common syntax of the AddHandler and its use with AddressOf. If you see my code snippet, my problem is I want to use AddressOf to somehow tie the handler of the button that was passed into the function (named "model" in the function call) to the handler to the newly created object (named "aButton" in my snippet). Please note that I don't know the model button control until run time -- if I knew the model button at design time I could use AddressOf easily.

See the bolded portion of my code snippet -- the "AddressOf model.Click" is what is failing. What I need to know is how to refer to the event handler of a control that was passed into the function as an argument. In this case, "model" is a reference to a button control that was passed into the function, and I want to find the address of its click event handler.


Thanks again,
parkerea
 
You clearly do not understand events as well as you think, since you are not passing the correct parameter to AddressOf. Model.Click is an event, not a method. You cannot tie an event to an event.

Think of a delegate (AddressOf returns an anonymous delegate) as a proxy or pointer to a local method.

If you want to tie the Click event of your new button to the handler of the model button, you need to pass the handler to AddressOf, not the event itself. The method you pass to AddressOf has to have a signature compatible with the event you are binding. In this case you cannot pass the model as a parameter to your function, as there is no way to extract the handler method just from the button. You can however declare the model outside the function, write the model handler, and then tie the Click event of your new button to that handler.

Dim Model As New Button

Public Function AddNewButton() As Button
    Dim aButton As New Button
    
    ...
    
    AddHandler aButton.Click, AddressOf ModelButtonHandler
    
    Return aButton
End Function

Public Function ModelButtonHandler(ByVal sender As Object, ByVal e As EventArgs) Handles Model.Click
    ...
End Function
 
Last edited:
I need to know is how to refer to the event handler of a control that was passed into the function as an argument.
That is not possible. Events exist for the very reason of preventing the delegate of being exposed and possibly harmed. Only the type that defines the event can access this delegate.
 
I think we misunderstood what you're trying to do because, quite frankly, it's rather misguided. There's no simple way to do it but, apparently, it can be done.

.net - How to Attach the Events of an Original Object to a Deep Copied Clone - Stack Overflow

What you should be doing is actually creating a collection to store the Buttons in. That collection can hold a reference to the event handler and it can add and remove them for you as you add and remove the items in the collection. You might take a look at this example of mine:

Control Arrays in VB.NET-VBForums

You could extend that to add events to the collection such that it managed the events of its items.
 
.net - How to Attach the Events of an Original Object to a Deep Copied Clone - Stack Overflow
Yeah, reflection is always good for ignoring good design :idea: - but wouldn't we hate to lose it?
Note also that this approach does not apply to types that declare custom events using EventHandlerList, which goes for most framework classes.
 
Ah yes reflection... In this particular example I do not think it would be necessarily bad though, as the function is only called when adding buttons (hopefully the compiler does not call through reflection every time the button is pressed; but even then, how fast can you really press a button...), but note that reflection is known for it's heavy performance penalty, so don't use it in loops unless you absolutely have to.
 
There is a fair amount to digest here, so it will take me a while to take it all in and respond. Please bear with me as I am an old dog learning new tricks. (My first program was in Fortran in 1975, on punch cards. Through the years I made it up to VB6 professionally, but am still adjusting to .NET in my spare time.)

===

Herman:

I presumed there is some way to refer to a control and in turn its handlers by passing it into a function. Sounds like I was incorrect.

I understand your example, but the problem is the ModelButtonHandler reference is specified at design time, but I won't know the model control or its handler until run time.

===

jmcilhinney:

I will dig into your example, though judging by the comments it sounds like your implementation may be fairly different from VB6 Control Arrays.

In my full code, the buttons are stored in a collection, following the method in the MS article I linked to. Maybe I can add the event handler to the collection also -- I will look into that.


Thanks again,
parkerea
 
Your first mistake is to try to replicate VB6 control arrays. They exist for a reason and that reason doesn't exist in VB.NET, hence control arrays don't exist. What are you actually trying to achieve here? Do you just want a bunch of Buttons whose Click events are all handled by the same method? Are different Buttons possibly going to have their events handled by different methods or is there just one Click event handler? If there's just one then why do you need to find out from some other Button what method is handling its Click event? You already know.
 
Back
Top