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
Related
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
I'm using Visual Studio 2013. My issue is that I can't get any value of my form1 from any class.
This is my code
Public MyHour as DateTime
Public SomeValue as string
Public MyClass as SomeClass
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
MyHour = datetime.now()
Somevalue = "asjdasd"
MyClass = new Someclass()
End Sub
Here is the button1 click event code in form1:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Myclass.mysub()
End sub
When I call a sub on my class I can't access to anything of my form1:
public sub mysub()
Dim datetovalidate as datetime= form1.actualhour
Dim stringtovalidate as string = form1.somevalue
messagebox.show(datetovalidate.tostring)
messagebox.show(stringtovalidate)
end sub
From my class:
Datetovalidate shows me: 01/01/0001 00:00:00
Stringtovalidate shows me : ""
Even If I try to access to a property of a control in my form1 from my class nothing happens, and there is no exception being fired and no errors show me up for this situation. What can I do?
I recommend you pass the parameters you need into the subroutine:
Public Sub MySub(ByVal myHour as DateTime, ByVal someValue as String)
messagebox.show(myHour.tostring)
messagebox.show(someValue)
End Sub
Then, you'll call it from Form1
Myclass.MySub(MyHour, SomeValue)
You could instead pass the whole Form1 in there, but that's less recommended in case you want to call the method from a different form, or from something that isn't a form at all.
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
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
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))