List(Of Event) in VB.NET - vb.net

The following is part of a larger project, but for the purpose of this question, I have the following code:
Public MustInherit Class Class1(Of T As {System.Windows.Forms.Control, New})
Inherits System.Windows.Forms.UserControl
Friend Items As New Dictionary(Of Integer, T)
Sub Add(ByRef Item As T, ByVal Index As Integer)
Me.Items.Add(Index, Item)
AddHandler Item.Click, AddressOf Class1Click
End Sub
Public Shadows Event Click(ByVal sender As System.Object, ByVal e As System.EventArgs, ByVal Index As Integer)
Sub Class1Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
RaiseEvent Click(sender, e, DirectCast(sender, T).Index)
End Sub
End Class
Public Class Class1CheckBox
Inherits Class1(Of System.Windows.Forms.CheckBox)
End Class
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
Partial Class Form1
Inherits System.Windows.Forms.Form
...
'NOTE: The following procedure is required by the Windows Form Designer
'It can be modified using the Windows Form Designer.
'Do not modify it using the code editor.
<System.Diagnostics.DebuggerStepThrough()> _
Private Sub InitializeComponent()
Me.MyClass1 = New Class1CheckBox()
Me.CheckBox1 = New System.Windows.Forms.CheckBox()
Me.CheckBox2 = New System.Windows.Forms.CheckBox()
Me.CheckBox3 = New System.Windows.Forms.CheckBox()
Me.CheckBox4 = New System.Windows.Forms.CheckBox()
Me.SuspendLayout()
...
Me.CheckBox1.Name = "CheckBox1"
Me.CheckBox2.Name = "CheckBox2"
Me.CheckBox3.Name = "CheckBox3"
Me.CheckBox4.Name = "CheckBox4"
...
End Sub
...
Friend WithEvents CheckBox1 As System.Windows.Forms.CheckBox
Friend WithEvents CheckBox2 As System.Windows.Forms.CheckBox
Friend WithEvents CheckBox3 As System.Windows.Forms.CheckBox
Friend WithEvents CheckBox4 As System.Windows.Forms.CheckBox
Friend WithEvents MyClass1 As Class1CheckBox
End Class
Public Class Form1
Private Sub MyClass1_Click(ByVal sender As Object, ByVal e As System.EventArgs, ByVal Index As Integer) Handles MyClass1.Click
MessageBox.Show(DirectCast(sender, CheckBox).Name)
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Me.Controls.OfType(Of CheckBox).AsParallel.ForAll(Sub(n) Me.MyClass1.Add(n, n.Index))
End Sub
End Class
The above code works beautifully. Anytime one of the four checkboxes are clicked, the click even is intercepted by the MyClass1 and is handled by MyClass1. That's what I want.
The problem is that "Click" is hardcoded. Notice, that Class1 is generic. I want for it to be able to accept any class which inherits System.Windows.Forms.Control. Some controls may have a Check event, or a hover, or a GotFocus. What I need is something as follows, I'm just not sure what the proper syntax is:
Public Class Class1CheckBox
Inherits Class1(Of System.Windows.Forms.CheckBox)
MyBase.AddEvent("Hover", <signature>...)
End Class
Public MustInherit Class Class1(Of T As {System.Windows.Forms.Control, New})
Inherits System.Windows.Forms.UserControl
Friend Items As New Dictionary(Of Integer, T)
Friend Events As New List(Of Event)
Sub AddEvent(EventName As String, ...)
Events.Add(EventName...)
End Sub
Sub Add(ByRef Item As T, ByVal Index As Integer)
Me.Items.Add(Index, Item)
For Each MyEvent As Event In Events
AddHandler ...
Next MyEvent
End Sub
'Public Shadows Event Click(ByVal sender As System.Object, ByVal e As System.EventArgs, ByVal Index As Integer)
'Sub Class1Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
' RaiseEvent Click(sender, e, DirectCast(sender, T).Index)
'End Sub
End Class
What is the proper syntax to create some kind of sequence of events, and how would I be able to raise those events?
Thanks,

What you want is not possible. Events are members, just like methods and properties. You can't write code to access any member unless you know that the type you have has that member.
Just as with methods and properties, if you want to decide what event to use at run time then you have to use Reflection.

As jmcilhinney correctly say, you need to use reflection.
Here is a simple example:
Imports System.Reflection
Public MustInherit Class Class1(Of T As {Control, New})
Private _Items As New Dictionary(Of Integer, T)
Private _Events As New Dictionary(Of String, [Event])
Protected Sub AddEvent(eventName As String, [delegate] As [Delegate])
If (Not Me._Events.ContainsKey(eventName)) Then
Dim info As EventInfo = GetType(T).GetEvent(eventName)
If (info Is Nothing) Then
Throw New ArgumentOutOfRangeException("eventName")
End If
Me._Events.Add(eventName, New [Event]([delegate], info))
End If
End Sub
Public Sub AddItem(item As T, index As Integer)
Me._Items.Add(index, item)
For Each [event] As KeyValuePair(Of String, [Event]) In Me._Events
[event].Value.Info.AddEventHandler(item, [event].Value.Delegate)
Next
End Sub
Friend Class [Event]
Friend Sub New([Delegate] As [Delegate], Info As EventInfo)
Me.[Delegate] = [Delegate]
Me.Info = Info
End Sub
Public ReadOnly [Delegate] As [Delegate]
Public ReadOnly Info As EventInfo
End Class
End Class
Public Class Class1CheckBox
Inherits Class1(Of CheckBox)
Public Sub New()
Me.AddEvent("CheckedChanged", New EventHandler(Sub(sender As Object, e As EventArgs) MsgBox(DirectCast(sender, CheckBox).Name & " is checked: " & DirectCast(sender, CheckBox).Checked.ToString())))
End Sub
End Class

Related

vb.net use function from other form

I am in a situation where i have a single helper class that i re-use on most of my forms. and it happens that my helper class needs to call a function from that form as well
Public Class Form1
Dim hc As HelperClass
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
hc = New HelperClass
End Sub
Private Sub someForm1Sub()
'do something
End Sub
End Class
Public Class Form2
Dim hc As HelperClass
Private Sub Form2_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
hc = New HelperClass
End Sub
Private Sub someForm2Sub()
'do something
End Sub
End Class
Public Class Form3
Dim hc As HelperClass
Private Sub Form3_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
hc = New HelperClass
End Sub
Private Sub someForm3Sub()
'do something
End Sub
End Class
Public Class HelperClass
'i need to call here someForm1Sub(), someForm2Sub(), someForm3Sub()
'but it bases on which form uses this class
End Class
i think that there are many ways to do this. I have read some about delegate function, but i do not know how to pass a delegate to other class. I am using vb.net 2010. Please provide the best way to do this
please reply thanks.
You can pass a delegate to a method using the AddressOf keyword. For example,
Public Sub Test()
' Make Foo() call the Bar() method.
Foo(AddressOf Bar)
End Sub
Public Sub Foo(d As Action)
' Call the delegate.
d()
End Sub
Public Sub Bar()
Console.WriteLine("Bar was called.")
End Sub
But, as you say, there are many ways you could do this. One possible solution which may be a bit nicer than passing delegates (especially if there are a few methods in the form that could be called by the helper class) would be to have Form1, Form2 and Form3 all implement an interface, and have the HelperClass call a method on the interface. An instance of the interface could then be given to HelperClass in its constructor.
For example,
Public Interface IForm
Sub DoStuff()
End Interface
Public Class Form2
Implements IForm
Dim hc As HelperClass
Private Sub Form2_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
hc = New HelperClass(Me)
End Sub
Public Sub DoStuff() Implements IForm.DoStuff
' Do stuff here.
End Sub
End Class
Public Class HelperClass
Private _form As IForm
Public Sub New(form as IForm)
_form = form
End Sub
Public Sub MakeTheFormDoStuff()
_form.DoStuff()
End Sub
End Class

Event from other class

I have to "catch" an event from another class but I can't.
Project contains two forms: Form1 and Form2 and class params. Form 1 contain one button which have to change property value in class props. And Form2 have to know when props raises event.
Here is situation:
''Form1 code
Public Class Form1
Dim p As New params
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Form2.Show()
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
p.demo = "mytry"
End Sub
End Class
''Form2 code
Public Class Form2
Private WithEvents p As params
Private Sub pChanged(ByVal propertyName As String) Handles p.PropChanged
MessageBox.Show(propertyName)
End Sub
End Class
''Class params code
Public Class params
Public Event PropChanged(ByVal propName As String)
Private _demo As String
Public Property demo() As String
Get
Return _demo
End Get
Set(ByVal value As String)
_demo = value
RaiseEvent PropChanged("demo")
End Set
End Property
End Class
Problem is that sub pChanged in Form2 is never fired and have to be fired when property "demo" raises an event PropChanged.
How to get this working?
You have two params variables and you are handling the wrong in Form2 if you change the property in Form1. You could handle the event in Form2 and raise it again to handle it in Form1.
One here:
Public Class Form1
Dim p As New params
and one here
Public Class Form2
Private WithEvents p As params
Note that you have to use WithEvents p As New params also in Form1 if you want to handle the event there.

Passing control from one class to an event handler from another class

I have a TextBox1 in Form1. I need to pass it to another class (in class libraries/another project). So, that instance of class can modify what inside TextBox1 within the entire class (not just a scope). For my problem i need to pass it to an event handler.
Public Class TheClass
WithEvents Timer1 As New Timer
Private Sub Timer1_Tick(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles Timer1.Tick
End Sub
End Class
I can think of passing by reference. But, I can't find a way to pass TextBox1 to that event handler.
What should I do to make the Timer1 have access to modify TextBox1 in Form1?
If TheClass is created after Form1, you can use constructor injection in order to pass a reference of Form1 to TheClass.
Public Class TheClass
Private WithEvents Timer1 As New Timer
Private m_form1 As Form1
Public Sub New (ByVal form1 as Form1)
m_form1 = form1
End Sub
Private Sub Timer1_Tick(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles Timer1.Tick
m_form1.TextBox1.Text = "tick"
End Sub
End Class
Note that TextBox1 must be Public or Friend.
Another way is to add a public event to TheClass.
Public Class TheClass
Public Event Tick()
Private WithEvents Timer1 As New Timer
Private Sub Timer1_Tick(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles Timer1.Tick
RaiseEvent Tick()
End Sub
End Class
Now the form can handle this event.
' In Form1
Private WithEvents theObj As New TheClass
Private Sub theObj_Tick() _
Handles theObj.Tick
Me.Textbox1.Text = "tick"
End Sub
Now you can keep Textbox1 private and TheClass does not need to know anything about a textbox.
The event handler can also have parameters
' In TheClass
Public Event Tick(ByVal counter As Integer)
...
Counter += 1
RaiseEvent Tick(Counter)
and
' In Form1
Private Sub theObj_Tick(ByVal counter As Integer) _
Handles theObj.Tick
Me.Textbox1.Text = "counter = " & counter
End Sub

inheritance in VB.net

Not real sure how to ask this question... Some of my terms may be incorrect but hopefully I'll be able to get the question across. If I have a class something like this
Public Class Agency
public property ID as integer=0
public property Name as string=string.empty
Public sub new()
end sub
end class
and then a factory class that returns a list
Public Class Agency_Controller
Public Sub New()
end sub
Public function Fetch() as list(of Agency)
pop the list and return it
end function
end class
If I create another class say Agency_Misc and want to inherit the Agency Class I get, how to do that?
Public Class Agency_Misc
inherits Agency
public property Address as string=string.empty
end class
Now if I want to use the Agency_Misc, how do I get the Agency_Controller Fetch function? In the code if I were going after the agency... I do something like
Dim oS as list(of Agency)=nothing
dim oC as new Agency_Controller
os=oc.Fetch()
but if I want my list to have the list(of Agency_Misc) (because I was doing some more stuff)
how do I do that. I can't change list(of Agency) to list(of Agency_Misc) because it will tell me it can't convert it I think it was.
Anyway... I'd like to learn what it is I'm missing or what other approach I need to look into.
If you have defined your classes properly, you can do the following:
Define a BaseClass.
Define a DerivedClass which inherits from BaseClass
Define a List(Of BaseClass) and populate it with objects of DerivedClass (the List will accept objects of Dervived class, because these are, through inheritance, also objects of BaseClass.
Access the items in the list with a variable of Type DerivedClass.
What you CAN'T do is populate the list with objects of type BaseClass, and then attempt to access them using a variable of Type DerivedClass.
Ex. #1 THIS will work:
Public Class Form1
Private MyListOfBaseCLass As List(Of MyBaseClass)
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
MyListOfBaseCLass = New List(Of MyBaseClass)
Dim dc As New MyDerivedCLass("City of Portland", "555 SW 5th Avenue")
MyListOfBaseCLass.Add(dc)
dc = New MyDerivedCLass("City of Salem", "222 E River Road")
MyListOfBaseCLass.Add(dc)
dc = New MyDerivedCLass("City of Denver", "333 SomeStreet")
MyListOfBaseCLass.Add(dc)
End Sub
Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
For Each dc As MyDerivedCLass In MyListOfBaseCLass
MsgBox(dc.MyName & ", " & dc.MyAddress)
Next
End Sub
End Class
THIS will FAIL:
Public Class Form1
Private MyListOfBaseCLass As List(Of MyBaseClass)
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
MyListOfBaseCLass = New List(Of MyBaseClass)
Dim dc As New MyBaseClass("City of Portland")
MyListOfBaseCLass.Add(dc)
dc = New MyBaseClass("City of Salem")
MyListOfBaseCLass.Add(dc)
dc = New MyBaseClass("City of Denver")
MyListOfBaseCLass.Add(dc)
End Sub
Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
For Each dc As MyDerivedCLass In MyListOfBaseCLass
MsgBox(dc.MyName & ", " & dc.MyAddress)
Next
End Sub
End Class
You best set the properties in the constructor
Public Class Agency
Private m_ID As Integer
Public Property ID() As Integer
Get
Return m_ID
End Get
Set(ByVal value As Integer)
m_ID = value
End Set
End Property
Private m_name As String
Public Property Name() As String
Get
Return m_name
End Get
Set(ByVal value As String)
m_name = value
End Set
End Property
Sub New(ByVal id As Integer, ByVal name As String)
Me.m_ID = id
Me.m_name = name
End Sub
End Class
...
Dim HiDollar as New Agency(100, "High Dollar")

Casting interfaces and MEF

I have the following problem with MEF:
Interface definition to be used by host:
Public Interface IExecuteDoSomething
Inherits IAddinSettings
Event DataReceived As EventHandler(Of DataReceivedEventArgs)
Function DoSomething() As Boolean
End Interface
Public Class DataReceivedEventArgs
Inherits EventArgs
Public Sub New(ByVal message As String)
Me.Message = message
End Sub
Public Message As String
End Class
extra interface needed by some other code inside the host:
Public Interface IAddinSettings
ReadOnly Property Setting() As AddinSettings
End Interface
Public Class AddinSettings
Private _Name As String
Public Property Name() As String
Get
Return _Name
End Get
Set(ByVal value As String)
_Name = value
End Set
End Property
Public Sub New(ByVal name As String)
Me.Name = name
End Sub
End Class
The class that provides the export:
<Export(GetType(SharedLibrary.IExecuteDoSomething))> Public Class Class1
Implements SharedLibrary.IExecuteDoSomething
Implements SharedLibrary.IAddinSettings
Private _Addinsettings As New SharedLibrary.Addinsettings("Test")
Public Function DoSomething() As Boolean Implements SharedLibrary.IExecuteDoSomething.DoSomething
MsgBox("i did something")
Return True
End Function
Public Event DataReceived(ByVal sender As Object, ByVal e As SharedLibrary.DataReceivedEventArgs) Implements SharedLibrary.IExecuteDoSomething.DataReceived
Public ReadOnly Property Setting() As SharedLibrary.AddinSettings Implements SharedLibrary.IAddinSettings.Setting
Get
Return _Addinsettings
End Get
End Property
End Class
The host:
Public Class Form1
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim catalog As New Hosting.AggregateCatalog
Dim d As New Hosting.DirectoryCatalog("..path to dlll..")
catalog.Catalogs.Add(d)
Dim container = New Hosting.CompositionContainer(catalog)
Dim batch As New Hosting.CompositionBatch
batch.AddPart(Me)
container.Compose(batch)
For Each dd In dos
AddHandler dd.DataReceived, AddressOf testevent
Next
End Sub
<Import()> Public dos As IEnumerable(Of SharedLibrary.IExecuteDoSomething)
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
For Each d In dos
d.DoSomething()
Next
End Sub
Private Sub testevent(ByVal sender As Object, ByVal e As SharedLibrary.DataReceivedEventArgs)
MsgBox("Event received: " & e.Message)
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Dosomethingelse(DirectCast(dos, System.Collections.Generic.List(Of SharedLibrary.IAddinSettings)))
End Sub
Private Sub Dosomethingelse(byval settings as IEnumerable(Of SharedLibrary.IAddinSettings))
End Sub
End Class
Everything seems to work fine until the Button2_Click routine is executed, then an InvalidCastException is thrown with the info:
Unable to cast object of type 'System.Collections.Generic.List1[SharedLibrary.IExecuteDoSomething]' to type 'System.Collections.Generic.List1[SharedLibrary.IAddinSettings]'.
How can i solve this problem, because the imported object implements both of the interfaces?
I suspect you're actually running into a covariance issue - that's the typical cause of problems like this. A List<IFoo> is not a List<IBar> even if IBar extends IFoo.
If you're using .NET 3.5, the easiest way to get round this in your case is to remove the DirectCast and instead use Enumerable.Cast:
Dosomethingelse(dos.Cast(Of SharedLibrary.IAddinSettings))