vb.net use function from other form - vb.net

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

Related

Share an event between 2 forms usign a common class

I would like to share an event to another form via a common class with a public shared event.
My code is setup like this:
Form1 is the main form where the button.click event is generated
Form2 is a subform displayed in Form1 in a panel control, where if the event is generated executes a sub.
To share data I use a commonData class, to share the only data i need, because I want to keep private function and variables in the single forms.
Can someone help me figure out what I'd like to do?
something like this, but working
Public Class commonData
Public Shared Event event1()
End class
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles button1.Click
RaiseEvent commonData.event1()
End Sub
End class
Public Class Form2
Private Sub eventFired(sender As Object, e As EventArgs) Handles commonData.event1
MsgBox("event")
End Sub
End class
There is many techniques to do that but this one (which is one of those) I hope might help you to have an idea how can have a shared Event between Forms or other classes.
First as you want here is the common module (you can use a class instead, is your choice; but <Extension()> go only in modules).
CommonData as a Class and ExtensionUtils as a Module for extensions:
Imports System.Runtime.CompilerServices
Public Class CommonData
Public Shared Event MyGlobalEvent(eventSender As Object, otherParams As String)
Shared Sub RaiseMyGlobalEvent(eventSender As Object, otherParams As String)
RaiseEvent MyGlobalEvent(eventSender, otherParams)
End Sub
End Class
Module ExtensionUtils
<Extension()>
Public Sub ButtonClick(ByVal buttonCaller As Button, eventArgs As String)
CommonData.RaiseMyGlobalEvent(buttonCaller, eventArgs)
End Sub
End Module
And here the implementation:
In this example I use a button named “Button1” in Form2 and when I click on it show a msgbox in Form1.
Form1:
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Form2.Show()
AddHandler CommonData.MyGlobalEvent, Sub(objSender As Object, message As String)
MsgBox(message & vbCrLf & " But I'm telling you that from form " & Me.Name)
End Sub
End Sub
End Class
Form2:
Public Class Form2
Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
AddHandler Button1.Click, Sub()
Button1.ButtonClick(("I'm clicked from form " & Me.Name))
End Sub
End Sub
End Class

VB.Net - Call a Sub with events

Is it possible to Call a Sub with events?
I have the following sub, and when I press a button, I will trigger a Msgbox.
Public Class SelectLevel
Public Shared Sub Button_Start_Click(sender As Object, e As EventArgs) Handles Button_Start.Click
MsgBox("Test")
End Sub
End Class
Can I Call this sub in another Class? Something along the lines of:
Public Class TestClass
Private Sub Testing
Call Button_Start_Click ' I tried this first, but it didn't work.
Call SelectLevel.Button_Start_Click ' I tried this aswell but didn't work.
End Sub
End Class
Thank you!
You need to pass the parameters too.
This should work:
Public Class TestClass
Private Sub Testing
Call SelectLevel.Button_Start_Click(Nothing, System.EventArgs.Empty)
End Sub
End Class
Your code is correct except in one place. While calling, you have to give the required parameters
Your Sub Procedure is : Public Shared Sub Button_Start_Click(sender As Object, e As EventArgs) Handles Button_Start.Click
But you're calling as :Call SelectLevel.Button_Start_Click (without any values to be sent)
Call like this:
Private Sub Testing()
SelectLevel.Button_Start_Click(Button_Start, Nothing) ' I tried this aswell but didn't work.
End Sub
This will do your work
If the event you need is only click event then there is an easy way PerformClick function
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Button1.PerformClick()
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
MsgBox("pressed")
End Sub
End Class

Early Binding with Generics

I have a class which takes the method address and arguments, and executes it later when told to do so.
' need to turn option strict off due to Execute method executing late-bound code
Option Strict Off
Public Class WorkItem
Private Action As Object
Private Args() As Object
Public Overloads Sub [Set](action As Action)
SetArgs(action)
End Sub
Public Overloads Sub [Set](Of T)(action As Action(Of T), arg As T)
SetArgs(action, arg)
End Sub
Public Overloads Sub [Set](Of T1, T2)(action As Action(Of T1, T2), arg1 As T1, arg2 As T2)
SetArgs(action, arg1, arg2)
End Sub
'*** more overloads of [Set] method go here...
Private Sub SetArgs(ByVal action As Object, ParamArray args() As Object)
Me.Action = action
Me.Args = args
End Sub
Public Sub Execute()
'-- early binding doesn't work
'DirectCast(Me.Action, Action(Of T)).Invoke(Args(0))
'-- this works, but forces me to to keep option strict off
Select Case Args.Length
Case 0 : Me.Action.Invoke()
Case 1 : Me.Action.Invoke(Args(0))
Case 2 : Me.Action.Invoke(Args(0), Args(1))
Case 3 : Me.Action.Invoke(Args(0), Args(1), Args(2))
Case 4 : Me.Action.Invoke(Args(0), Args(1), Args(2), Args(3))
End Select
End Sub
End Class
Here is some tester code:
Public Class Form1
Dim TheTask As WorkItem
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
TheTask = New WorkItem
TheTask.Set(AddressOf DummyProc, TextBox1)
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
TheTask.Execute()
End Sub
Private Sub DummyProc(arg As TextBox)
Threading.Thread.Sleep(1000)
Debug.Print("work completed")
End Sub
End Class
The WorkItem class obviously doesn't work with OPTION STRICT ON, due to the late-bound call in Execute method.
Is there any way I can convert the late-bound call to early binding?
This can be achieved by using delegates. You can declare a delegate that represents a non-generic subroutine, and invoke a representation of that delegate on an object that passed arg as a constructor parameter. I don't know if your solution still needs a generic work item by implementing it like this, but my example still keeps that same setup:
Delegate Sub WorkItem()
Dim TheTask As WorkItem
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
TheTask = AddressOf (New DummyProc(Of TextBox)(TextBox1)).Execute
End Sub
Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
TheTask.Invoke()
End Sub
Private Class DummyProc(Of T)
Private ReadOnly _arg As T
Public Sub New(ByVal arg As T)
_arg = arg
End Sub
Public Sub Execute()
MessageBox.Show(String.Format("My Arg: {0}", _arg))
Threading.Thread.Sleep(1000)
MessageBox.Show("work completed")
End Sub
End Class

Polymorphism in VB.NET via Late Binding disallows With Events, workaround?

I'm working on developing an application that talks to a family of USB sensors. I've created a basic implementation that utilizes a class called Sensor. The class contains events and methods that allow for interaction with the sensor (there is also a threaded task processor involved but I'll go with a simple example).
My issue is that this simple proof of concept example works fine but now I need to expand the application to support the whole family of sensors. To do this I've created a BaseSensor class with all the appropriate methods and events and then I've created multiple subclasses such as SensorA, SensorB, and SensorC that all inherent BaseSensor.
This seemed like a good application of polymorphism so I've created a Shared function on BaseSensor called Initialize that does an initial USB communication and returns the correct object depending on the sensor type (SensorA, SensorB, SensorC). This works fine however it seems I can not find a way to correctly declare the object With Events. See the sample code for my deliema.
Attempt 1:
Public Class Form1
Dim WithEvents oBaseClass As BaseClass
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
oBaseClass = New ExtendedClass
oBaseClass.Test() 'This doesn't work because the object was type casted.
End Sub
Private Sub TestEventHdlr() Handles oBaseClass.TestEvent
MsgBox("Event Fired")
End Sub
End Class
Public Class BaseClass
Public Event TestEvent()
End Class
Public Class ExtendedClass
Inherits BaseClass
Public Sub Test()
MsgBox("Test")
End Sub
End Class
Attempt 2:
Public Class Form1
Dim WithEvents oBaseClass 'This doesn't work.
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
oBaseClass = New ExtendedClass
oBaseClass.Test()
End Sub
Private Sub TestEventHdlr() Handles oBaseClass.TestEvent
MsgBox("Event Fired")
End Sub
End Class
Public Class BaseClass
Public Event TestEvent()
End Class
Public Class ExtendedClass
Inherits BaseClass
Public Sub Test()
MsgBox("Test")
End Sub
End Class
I'm missing something here. How should I proceed?
WithEvents can not be late bound. You need to declare your field with a type. If all objects used in this scenario derive from a common base, you will be doing yourself a huge favor in ditching late binding. Cast when necessary, and declare virtual (overridable) methods to implement your polymorphism.
Public Class Form1
Dim WithEvents oBaseClass As BaseClass 'Early bound'
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
oBaseClass = New ExtendedClass
DirectCast(oBaseClass, ExtendedClass).Test() 'Casting to call a method'
End Sub
Private Sub TestEventHdlr() Handles oBaseClass.TestEvent
MsgBox("Event Fired")
End Sub
End Class
Public Class BaseClass
Public Event TestEvent()
End Class
Public Class ExtendedClass
Inherits BaseClass
Public Sub Test()
MsgBox("Test")
End Sub
End Class
is this what your looking for?
Public Class Form1
Private WithEvents oBaseClass As ExtendedClass
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
oBaseClass = New ExtendedClass
oBaseClass.Test()
End Sub
Private Sub TestEventHdlr() Handles oBaseClass.TestEvent
MsgBox("Event Fired")
End Sub
End Class
Public Class BaseClass
Public Event TestEvent()
Friend Sub raiseTestEvent()
RaiseEvent TestEvent()
End Sub
End Class
Public Class ExtendedClass
Inherits BaseClass
Public Sub Test()
MsgBox("Test")
raiseTestEvent()
End Sub
End Class
if not, you need to create an interface and declare it in your form and setting the corresponding class to it in the form_load
Adjusted slightly to raise the event in case you wanted an example of that, altered the event a bit as well to provide a bit different approach.
Public Class Form1
Dim oBaseClass As BaseClass 'Early bound'
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
oBaseClass = New ExtendedClass
If TypeOf oBaseClass Is BaseClass Then
AddHandler DirectCast(oBaseClass, BaseClass).TestEvent, AddressOf TestEventHdlr
End If
oBaseClass.Test()
End Sub
Private Sub TestEventHdlr()
MsgBox("Event Fired")
End Sub
End Class
Public Class BaseClass
Public Event TestEvent()
Public Overridable Sub Test()
RaiseEvent TestEvent()
End Sub
End Class
Public Class ExtendedClass
Inherits BaseClass
Public Overrides Sub Test()
MyBase.Test()
MsgBox("Test")
End Sub
End Class
Depending on the types of behaviour you're looking to model, you might want to consider using Interfaces instead of Inheritence or a mixture of both. Rather than having a base object that defines the event, define behaviours or functions based around interfaces and implement those in the equivilent of your ExtendedClass
This annoyed me a lot too but it kind of forced me to learn about them and as you can implement multiple interface types per object it's ultimately very flexible.
One tip though: think about interface content first. Although VS auto inserts method/property stubs for you, if you later change the definition of the interface it's a manual effort to update the implementations.
Simon

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))