How to Pass Additional Parameters When Calling and Event VB.net - vb.net

Public Event DocumentCompleted As WebBrowserDocumentCompletedEventHandler
Dim arg() As Object = {homeTeam, guestTeam}
AddHandler browser.DocumentCompleted, New
WebBrowserDocumentCompletedEventHandler(AddressOf DoStuff)
Private Sub DoStuff(ByVal sender As Object, ByVal e As WebBrowserDocumentCompletedEventArgs)
End Sub
How can I pass the homeTeam and guestTeam when firing the DocumentCompleted event.
I want to ge the above to values to inside the Dostuff method.
Please help.

First of all, you cannot have this hanging in the middle of nowhere:
Dim arg() As Object = {homeTeam, guestTeam}
AddHandler browser.DocumentCompleted,
New WebBrowserDocumentCompletedEventHandler(AddressOf DoStuff)
AddHandler probably needs to be in some Initialize method, which could be inside Sub New, after InitializeComponent, or inside Form_Load, or as soon as you expect it to be triggered (after a specific event). Notice here that you are using a default event of a native .NET component, with a default event type. In this case you cannot directly consume anything other than what it already provides, when triggered. See WebBrowser.DocumentCompleted Event on MSDN.
You can, however, override all relevant classes and have your own MyWebBrowser control and your own event, with would contain additional properties. See below example:
Public Class Form1
Sub New()
' This call is required by the designer.
InitializeComponent()
Dim browser As New MyWebBrowser
AddHandler browser.MyDocumentCompleted, AddressOf DoStuff
End Sub
Private Sub DoStuff(ByVal sender As Object, ByVal e As MyWebBrowserDocumentCompletedArgs)
Dim guestTeam As String = e.GuestTeam 'guest team
Dim homeTeam As String = e.HomeTeam 'and home team are both accessible
'so you can do some processing on them
End Sub
Public Class MyWebBrowserDocumentCompletedArgs : Inherits WebBrowserDocumentCompletedEventArgs
Dim _homeTeam As String
Dim _guestTeam As String
Public ReadOnly Property HomeTeam
Get
Return _homeTeam
End Get
End Property
Public ReadOnly Property GuestTeam
Get
Return _guestTeam
End Get
End Property
Sub New(url As Uri, homeTeam As String, guestTeam As String)
MyBase.New(url)
_homeTeam = homeTeam
_guestTeam = guestTeam
End Sub
End Class
Public Class MyWebBrowser : Inherits WebBrowser
Public Delegate Sub MyWebBrowserDocumentCompletedEventHandler(e As MyWebBrowserDocumentCompletedArgs)
Public Event MyDocumentCompleted As MyWebBrowserDocumentCompletedEventHandler
Protected Overrides Sub OnDocumentCompleted(e As System.Windows.Forms.WebBrowserDocumentCompletedEventArgs)
MyBase.OnDocumentCompleted(e)
'homeTeam and guestTeam need to be extracted from the current instance of MyWebBrowser, and passed further
RaiseEvent MyDocumentCompleted(New MyWebBrowserDocumentCompletedArgs(e.Url, "homeTeam", "guestTeam"))
End Sub
End Class
End Class
If your project is relatively small, you can indeed have those as global variables, as #Vlad suggested in the comments.

Related

Getting Event Handler from Instance of Class

I'm just getting started with custom classes so I wrote a button strip. It inherits a panel and populates it with buttons from strings passed to my .Add().
ButtonStrip1.Add("Button","Texts","Go Here")
I'd like for it to be able to naturally grab ButtonStrip1.Click's handler and pass it on to child buttons and delete it from ButtonStrip1.
I haven't been able to figure that out, so I've been doing
Public Class ButtonStrip
Inherits Panel
Public Property Innermargin As Integer = 5
Dim Offset As Integer = Innermargin
Dim Buttons = New List(Of ButtonStrip_Button)
Dim StoredFN As EventHandler
Public Sub New()
End Sub
Function Add(fn As EventHandler, ParamArray ByVal Values As String())
StoredFN = fn
For Each V In Values
Dim B As New ButtonStrip_Button
Buttons.Add(B)
Me.Controls.Add(B)
B.Text = V
B.Left = Offset + Innermargin
B.Top = Innermargin
Offset = B.Left + B.Width
AddHandler B.Click, fn
Next
RemoveHandler Me.Click, fn
Me.Width = Offset + Innermargin
Me.Height = Buttons(0).height + Innermargin * 2
End Function
Function Add(ParamArray ByVal Values As String())
If StoredFN Is Nothing Then
Throw New Exception("First Add() must supply a function")
End If
Me.Add(StoredFN, Values)
End Function
End Class
Public Class ButtonStrip_Button
Inherits System.Windows.Forms.Button
Public Sub New()
AutoSize = True
AutoSizeMode = AutoSizeMode.GrowAndShrink
End Sub
End Class
which is called by
ButtonStrip1.Add(AddressOf ButtonStrip1_Click,"Button","Texts","Go Here")
What I'd basically like to do is (psuedo-code)
Function Add(fn As EventHandler, ParamArray ByVal Values As String())
If StoredFN is Nothing Then StoredFN = Me.Click
...
AddHandler B.Click, Me.Click
Next
RemoveHandler Me.Click, Me.Click
...
End Function
I've tried changing a few things and googled a lot. I've also tried using CallByName(Me,"Click",CallType.Method) and with CallType.Get, but the error I get is Expression 'Click' is not a procedure, but occurs as the target of a procedure call. It also returns this exact same message for unhandled events, such as ButtonStrip1 has no MouseDown Event.
I've also tried using MyClass.
Not seen here is an alternative .Add() that add StoredFN to B.click
For instance, this click event works with my code
Private Sub ButtonStrip1_Click(sender As Object, e As EventArgs) Handles ButtonStrip1.Click
msgbox("You clicked " & sender.text & ".")
End Sub
What I was suggesting was something like this:
Public Class ButtonStrip
Inherits Panel
Public Event ButtonClick As EventHandler(Of ButtonClickEventArgs)
Protected Overridable Sub OnButtonClick(e As ButtonClickEventArgs)
RaiseEvent ButtonClick(Me, e)
End Sub
Private Sub Buttons_Click(sender As Object, e As EventArgs)
OnButtonClick(New ButtonClickEventArgs(DirectCast(sender, Button)))
End Sub
Public Sub Add(ParamArray values As String())
Dim btn As New Button
AddHandler btn.Click, AddressOf Buttons_Click
'...
End Sub
End Class
Public Class ButtonClickEventArgs
Inherits EventArgs
Public ReadOnly Property Button As Button
Public Sub New(button As Button)
Me.Button = button
End Sub
End Class
Now there's no need to pass event handlers around. When a Button is clicked, the ButtonStrip handles that event and then raises its own ButtonClick event. Neither the ButtonStrip nor the Buttons have to care about any methods that handle that event as it will be handled the same way any other event is. In a form, you'd then handle the ButtonClick event of the ButtonStrip and get the Button that was clicked from e.Button. You could also add an Index property if you wanted to know the position of the Button rather than the Button itself.

Need to pass object through dynamically created event handler

Need to pass object through dynamically created event handler in vb.net application, (newperson is a usercontrol). The event handler:
AddHandler newperson.MouseUp, AddressOf newperson_MouseUp
Then I use this handler as follows,
Private Sub newperson_MouseUp()
End sub
But I need to refer to the newperson within this sub, e.g.
newperson.Background = Brushes.Black
Any input or ideas will be appreciated :).
You need three things:
A custom EventArgs class where you store a reference to your newPerson object
Public Class MyMouseUpEventArgs
Inherits MouseEventArgs
Public Sub New(newPerson As Person, b As MouseButtons, clicks As Integer, x As Integer, y As Integer, Delta As Integer)
MyBase.New(b, clicks, x, y, Delta)
Me.newPerson = newPerson
End Sub
Public Property newPerson As Person
End Class
It inherits from MouseEventArgs to also have the default event args, but this is not a must have.
In your custom control you need to handle the original MouseUp event. In this handler you simply raise a new custom event which I called MyMouseUp. This custom event takes as parameter the previously created MyMouseEventArgs with the new person
Public Class Person
Inherits UserControl
Public Shared Event MyMouseUp(sender As Object, e As MyMouseUpEventArgs)
Public Sub New()
AddHandler Me.MouseUp, AddressOf OnMouseUp
End Sub
Private Overloads Sub OnMouseUp(sender As Object, e As MouseEventArgs)
RaiseEvent MyMouseUp(sender, New MyMouseUpEventArgs(Me, e.Button, e.Clicks, e.X, e.Y, e.Delta))
End Sub
End Class
The custom event must be shared so it can be used in the handler class without object reference shown in step
The handler (here just the main form) now registers to the new custom event with the custom event args. Thus you can access newPerson.
Public Class Form1
Public Sub New()
InitializeComponent()
AddHandler Person.MyMouseUp, AddressOf OnyMyMouseUp
End Sub
Private Overloads Sub OnyMyMouseUp(sender As Object, e As MyMouseUpEventArgs)
'Do stuff
e.newPerson.BackColor = Color.Aqua
End Sub
End Class
Hope that helps. The code is not tested so no warranty is given ;)

Safe ThreadPool Queueing with Parameters in VB.NET (WinForms)

I know how to use BackgroundWorker (gui object in WinForms designer), and to manually instantiate Threads that elevate the custom event to the UI, however, I am having some trouble figuring out how to use the ThreadPool object (simplest form) to handle elevating an event to the form for "safe" UI manipulation.
Example is as follows :
Form1.vb
Public Class Form1
WithEvents t As Tools = New Tools
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
t.Unzip("file 1", "foo")
t.Unzip("file 2", "foo")
t.Unzip("file 3", "foo")
t.Unzip("file 4", "foo")
t.Unzip("file 5", "foo")
t.Unzip("file 6", "foo")
t.Unzip("file 7", "foo")
t.Unzip("file 8", "foo")
t.Unzip("file 9", "foo")
End Sub
Private Sub t_UnzipComplete(ZipInfo As Tools.ZipInfo) Handles t.UnzipComplete
TextBox1.Text = TextBox1.Text & ZipInfo.ZipFile & vbCr
End Sub
End Class
( add a multiline textbox, and a button to this form for the demo )
Tools.vb
Imports System
Imports System.Threading
Imports System.IO.Compression
Public Class Tools
#Region "Zip"
Private _zip As System.IO.Compression.ZipFile
Public Shared Event UnzipComplete(ByVal ZipInfo As ZipInfo)
Public Shared Event ZipComplete(ByVal ZipInfo As ZipInfo)
Public Class ZipInfo
Public Property ZipFile As String
Public Property Path As String
End Class
Public Sub Unzip(ByVal ZipFile As String, ByVal Destination As String)
Dim _ZipInfo As New Tools.ZipInfo
_ZipInfo.ZipFile = ZipFile
_ZipInfo.Path = Destination
ThreadPool.QueueUserWorkItem(AddressOf ThreadUnzip, _ZipInfo)
End Sub
Public Sub Zip(ByVal Folder As String, ByVal ZipFile As String)
Dim _ZipInfo As New Tools.ZipInfo
_ZipInfo.ZipFile = ZipFile
_ZipInfo.Path = Folder
ThreadPool.QueueUserWorkItem(AddressOf ThreadUnzip, _ZipInfo)
End Sub
Shared Sub ThreadUnzip(ZipInfo As Object)
RaiseEvent UnzipComplete(ZipInfo)
End Sub
Shared Sub ThreadZip(ZipInfo As Object)
RaiseEvent ZipComplete(ZipInfo)
End Sub
#End Region
End Class
What this code should do, is as follows :
On Button1_Click, add 9 items to the ThreadPool
On each thread completion (order is irrelevant), raise an event that elevates to Form1
The event being raised on Form1 should be UI safe, so I can use the information being passed to the ZipCompleted / UnzipCompleted events in the Textbox. This should be generic, meaning the function that raises the event should be reusable and does not make calls to the form directly. (aka, I do not want a "custom" sub or function in Tools.vb that calls specific elements on Form1.vb . This should be generic and reusable by adding the class to my project and then entering any "custom" form code under the event being raised (like when Button1_Click is raised, even though it's threaded, the other form interactions are not part of the Button1 object/class -- they are written by the coder to the event that is raised when a user clicks.
If you want to ensure that an object that has no direct knowledge of your UI raises its events on the UI thread then use the SynchronizationContext class, e.g.
Public Class SomeClass
Private threadingContext As SynchronizationContext = SynchronizationContext.Current
Public Event SomethingHappened As EventHandler
Protected Overridable Sub OnSomethingHappened(e As EventArgs)
RaiseEvent SomethingHappened(Me, e)
End Sub
Private Sub RaiseSomethingHappened()
If Me.threadingContext IsNot Nothing Then
Me.threadingContext.Post(Sub(e) Me.OnSomethingHappened(DirectCast(e, EventArgs)), EventArgs.Empty)
Else
Me.OnSomethingHappened(EventArgs.Empty)
End If
End Sub
End Class
As long as you create your instance of that class on the UI thread, its SomethingHappened event will be raised on the UI thread. If there is no UI thread then the event will simply be raised on the current thread.
Here's a more complete example, which includes a simpler method for using a Lambda Expression:
Imports System.Threading
Public Class Form1
Private WithEvents thing As New SomeClass
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Me.thing.DoSomethingAsync()
End Sub
Private Sub thing_DoSomethingCompleted(sender As Object, e As IntegerEventArgs) Handles thing.DoSomethingCompleted
MessageBox.Show(String.Format("The number is {0}.", e.Number))
End Sub
End Class
''' <summary>
''' Raises events on the UI thread after asynchronous tasks, assuming the instance was created on a UI thread.
''' </summary>
Public Class SomeClass
Private ReadOnly threadingContext As SynchronizationContext = SynchronizationContext.Current
Public Event DoSomethingCompleted As EventHandler(Of IntegerEventArgs)
''' <summary>
''' Begin an asynchronous task.
''' </summary>
Public Sub DoSomethingAsync()
Dim t As New Thread(AddressOf DoSomething)
t.Start()
End Sub
Protected Overridable Sub OnDoSomethingCompleted(e As IntegerEventArgs)
RaiseEvent DoSomethingCompleted(Me, e)
End Sub
Private Sub DoSomething()
Dim rng As New Random
Dim number = rng.Next(5000, 10000)
'Do some work.
Thread.Sleep(number)
Dim e As New IntegerEventArgs With {.Number = number}
'Raise the DoSomethingCompleted event on the UI thread.
Me.threadingContext.Post(Sub() OnDoSomethingCompleted(e), Nothing)
End Sub
End Class
Public Class IntegerEventArgs
Inherits EventArgs
Public Property Number() As Integer
End Class
You should register from the Form to events of the Tools class (you already have these events defined), of course the actual event will be fired under a non-UI thread, so the code it executes during the callback will only be able to update the UI via an Invoke()
You want to simply raise the event in the Tools class, the Invoke needs to be done because you want to update the UI, the Tools class should be concerned about that.
Change your event handling like so:
Private Sub t_UnzipComplete(ZipInfo As Tools.ZipInfo) Handles t.UnzipComplete
TextBox1.Invoke(Sub () t_UnzipComplete(ZipInfo))
End Sub
To register to the event from the view: (this would go in the Button1_Click event
AddHandler t.UnzipComplete, AddressOf t_UnzipComplete
Make sure you only register to the event one time
Does this solve your issue?
Private Sub t_UnzipComplete(ZipInfo As Tools.ZipInfo) Handles t.UnzipComplete
If TextBox1.InvokeRequired Then
TextBox1.Invoke(Sub () t_UnzipComplete(ZipInfo))
Else
TextBox1.Text = TextBox1.Text & ZipInfo.ZipFile & vbCr
End If
End Sub
You could create a callback to do the invoking in a safer way. Something like this:
Public Sub Unzip(ByVal ZipFile As String, ByVal Destination As String, _
ByVal SafeCallback As Action(Of ZipInfo))
And then the calling code does this:
t.Unzip("file 1", "foo", Sub (zi) TextBox1.Invoke(Sub () t_UnzipComplete(zi)))
Personally I think it is better - and more conventional - to invoke on the event handler, but you could do it this way.
Okay, so here is what I came up with using a combination of the information from everyone contributing to this question -- all excellent and VERY helpful answers, which helped lead me to the final solution. Ideally, I would like this as a straight "class", but I can accept a UserControl for this purpose. If someone can take this and do exactly the same thing with a class, that would definitely win my vote. Right now, I will really have to consider which one to vote for.
Here is the updated Tools.vb
Imports System
Imports System.Threading
Imports System.Windows.Forms
Imports System.IO.Compression
Public Class Tools
Inherits UserControl
#Region "Zip"
Private _zip As System.IO.Compression.ZipFile
Private threadingContext As SynchronizationContext = SynchronizationContext.Current
Private Delegate Sub EventArgsDelegate(ByVal e As ZipInfo)
Public Shared Event UnzipComplete(ByVal ZipInfo As ZipInfo)
Public Shared Event ZipComplete(ByVal ZipInfo As ZipInfo)
Public Class ZipInfo
Public Property ZipFile As String
Public Property Path As String
End Class
Public Sub Unzip(ByVal ZipFile As String, ByVal Destination As String)
Dim _ZipInfo As New Tools.ZipInfo
_ZipInfo.ZipFile = ZipFile
_ZipInfo.Path = Destination
ThreadPool.QueueUserWorkItem(AddressOf ThreadUnzip, _ZipInfo)
End Sub
Public Sub Zip(ByVal Folder As String, ByVal ZipFile As String)
Dim _ZipInfo As New Tools.ZipInfo
_ZipInfo.ZipFile = ZipFile
_ZipInfo.Path = Folder
ThreadPool.QueueUserWorkItem(AddressOf ThreadUnzip, _ZipInfo)
End Sub
Private Sub ThreadUnzip(ZipInfo As Object)
If Me.InvokeRequired Then
Me.Invoke(New EventArgsDelegate(AddressOf ThreadUnzip), ZipInfo)
Else
RaiseEvent UnzipComplete(ZipInfo)
End If
End Sub
Private Sub ThreadZip(ZipInfo As Object)
If Me.InvokeRequired Then
Me.Invoke(New EventArgsDelegate(AddressOf ThreadZip), ZipInfo)
Else
RaiseEvent ZipComplete(ZipInfo)
End If
End Sub
#End Region
End Class
If you drop this on Form1.vb, and select/activate the UnzipComplete/ZipComplete events, you will find that they will interact with the UI thread without having to pass a Sub, or Invoke, etc, from the Form. It is also generic, meaning it is unaware of what form elements you will be interacting with so explicit invoking such as TexBox1.Invoke() or other element specific calls are not required.

How do I get an event to fire as soon as an object's constructor has finished?

Research tells me that raising an event from the constructor itself is not feasible as the object may not be fully initialised... so where can I fire an event from as soon as the constructor has fired?
One thing you can do is add a method to handle additional post ctor tasks:
Friend Class FooBar
Public Sub New
' your code here
End Sub
Public Sub Create
' do anything you want
End Sub
End Class
Elsewhere:
Friend WithEvents Foo As Foobar
' ...
Foo = New FooBar ' Foo doesnt exist until ctor code executes and the
' code returns to here.
Foo.Create ' do whatever you like, as long as any other
' objects referenced have been created.
The reason calling a sub from the ctor to raise an event wont work with a class is this:
Private Sub SomeEvent(sender As Object, e As EventArgs) Handles Foo.SomeEvent
Console.Beep()
End Sub
the key is Handles Foo.SomeEvent
There is no Foo yet to handle the event. It doesnt crash and there event is raised, but there is no object for the listener to catch/handle the event. Enough of a form is created in InitializeComponents, that it does work with a form.
There might also be an Interface to implement something like this, I know of some for Components, but not classes.
You could use the Load or Show events from the Shown.
Private Sub myForm_Shown(sender As Object, e As EventArgs) Handles Me.Shown
End Sub
or
Private Sub myForm_Load(sender As Object, e As EventArgs) Handles Me.Load
End Sub
You can accomplish this by adding an Action(Of T) parameter to your constructor and invoke the delegate on the very last line.
Public Class Foo
Public Sub New(ByVal action As Action(Of Foo))
'...
'...
'...
If (Not action Is Nothing) Then action.Invoke(Me)
End Sub
End Class
Example
Public Class Form1
Private Sub Button1_Click(sender As Object, ev As EventArgs) Handles Button1.Click
Dim foo1 As New Foo("foo1", AddressOf Me.HandleFooCtor)
Dim foo2 As New Foo("foo2", Sub(f As Foo) MessageBox.Show(f.Name))
End Sub
Private Sub HandleFooCtor(f As Foo)
MessageBox.Show(f.Name)
End Sub
Public Class Foo
Public Sub New(name As String, Optional ByVal action As Action(Of Foo) = Nothing)
'...
'...
'...
Me.Name = name
If (Not action Is Nothing) Then action.Invoke(Me)
End Sub
Public ReadOnly Name As String
End Class
End Class

AddressOf with parameter

One way or another I need to link groupID (and one other integer) to the button I am dynamically adding.. any ideas?
What I can do;
AddHandler mybutton.Click, AddressOf PrintMessage
Private Sub PrintMessage(ByVal sender As System.Object, ByVal e As System.EventArgs)
MessageBox.Show("Dynamic event happened!")
End Sub
What I can't do, but want to;
AddHandler mybutton.Click, AddressOf PrintMessage(groupID)
Private Sub PrintMessage(ByVal groupID as Integer)
MessageBox.Show("Dynamic event happened!" & groupID .tostring)
End Sub
There is no way to do this with AddressOf itself. What you're looking for is a lambda expression.
AddHandler myButton.Click, Function(sender, e) PrintMessage(groupId)
Private Sub PrintMessage(ByVal groupID as Integer)
MessageBox.Show("Dynamic event happened!" & groupID .tostring)
End Sub
You can create your own button class and add anything you want to it
Public Class MyButton
Inherits Button
Private _groupID As Integer
Public Property GroupID() As Integer
Get
Return _groupID
End Get
Set(ByVal value As Integer)
_groupID = value
End Set
End Property
Private _anotherInteger As Integer
Public Property AnotherInteger() As Integer
Get
Return _anotherInteger
End Get
Set(ByVal value As Integer)
_anotherInteger = value
End Set
End Property
End Class
Since VB 2010 you can simply write
Public Class MyButton
Inherits Button
Public Property GroupID As Integer
Public Property AnotherInteger As Integer
End Class
You can access the button by casting the sender
Private Sub PrintMessage(ByVal sender As Object, ByVal e As EventArgs)
Dim btn = DirectCast(sender, MyButton)
MessageBox.Show( _
String.Format("GroupID = {0}, AnotherInteger = {1}", _
btn.GroupID, btn.AnotherInteger))
End Sub
These new properties can even be set in the properties window (under Misc).
The controls defined in the current project automatically appear in the toolbox.
Use the Tag property of the button.
Button1.Tag = someObject
AddressOf gets the address of a method, and thus you cannot pass parameters to it.
You can use delegate which very clear for your code follow as:
Define a delegate
Public Delegate Sub ControlClickDelegate(ByVal sender As Object, ByVal e As EventArgs)
Custom button class
Public Class CustomButton
Inherits System.Windows.Forms.Button
#Region "property delegate"
Private controlClickDelegate As ControlClickDelegate
Public Property ClickHandlerDelegate As ControlClickDelegate
Get
Return controlClickDelegate
End Get
Set(ByVal Value As ControlClickDelegate)
controlClickDelegate = Value
End Set
End Property
#End Region
Public Sub RegisterEventHandler()
AddHandler Me.Click, AddressOf OnClicking
End Sub
Private Sub OnClicking(ByVal sender As Object, e As System.EventArgs)
If (Me.controlClickDelegate IsNot Nothing) Then
Me.controlClickDelegate(sender, e)
End If
End Sub
End Class
MainForm
Public Class MainForm
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Me.CusButton1.ClickHandlerDelegate = AddressOf Me.btnClick
Me.CusButton1.RegisterEventHandler()
End Sub
Private Sub btnClick(ByVal sender As Object, ByVal e As EventArgs)
Me.TextBox1.Text = "Hello world"
End Sub
End Class
The below worked for me:
Dim bStart = New Button With {.Text = "START"}
AddHandler bStart.Click, Function(sender, e) TriggerProcess(any Long value)
Private Function TriggerProcess(ByVal paramName As Long) As Boolean
' any processing logic
Return True
End Function
My solution:
AddHandler menuItemYear.Items(i).MouseUp, Sub() menu_year(2019)
Private Sub menu_year(ByVal intYear As Integer)
'do something
End Sub
There are few ways to do that depending of the complexity and number of parameters required.
1. Use Tag for adding a complex structure
2. Inherit the the Button class and add the values as class members then populate them before using it. That gives you a lot more flexibility.
If you are using web version
3. You cannot add it to Tag, but for simple values assign it to index use .Attributes.Add("name"). This gets added to the HTML tags and not the Server side. You can then use the index to access a server side structure for complex systems.
4. Use sessions to store values and store the session reference to Name attribute as described above (#3).
No problem ;-)
For example:
Private ComboActionsOnValueChanged As New Dictionary(Of ComboBox, EventHandler)
'somewhere in function
dim del = Sub(theSender, eventArgs)
MsgBox(CType(theSender, ComboBox).Name & " test")
End Sub
ComboActionsOnValueChanged.Add(myCombo, del)
'somewhere else
Dim delTest = ComboActionsOnValueChanged(myCombo)
RemoveHandler myCombo.SelectedValueChanged, delTest
myCombo.DataSource = someDataSource
AddHandler myCombo.SelectedValueChanged, delTest
as we expect, event won't fire after DataSource change in this place