C# to VB.net delegate sub - vb.net

I'm continuing to try to learn VB.net, and I visit here often.
So, I'm back again, with a slightly different question. I received great help previously, and hope to avail myself again.
I have this CSharp code:
public delegate void MyHandler(string DataLine);
foo.Handler = new MyHandler(MySub);
void MySub(string DataLine);
(When I hover over "MyHandler", the VS helper says "MyHandler.MyHandler(void (string) target)"
I want to set up the delegate in VB.net, but I can't seem to relate the three items so they all work together:
Private Delegate Sub MyHandler(ByRef DataLine as string)
?????? as MyHandler
Private Sub MySub (ByRef DataLine as string)
Does the "??????" need to be a Dim {something}, or a Declare {something} or ??? Or is there an error in one of the other two lines?
(Let me know if there is any missing info)
Thanks
Charlie

In VB.Net, you can use AddressOf to assign a Sub to a Delegate:
foo.Handler = AddressOf MySub

Given this:
Private Delegate Sub MyHandler(ByRef DataLine as string)
Private Sub MySub(ByRef DataLine as string)
' Do something here
End Sub
You can declare and instantiate the delegate, like this:
Dim TheHandler As MyHandler
TheHandler = AddressOf MySub

Related

How to apply threading in vb.net with passing an object of a custom class for a method

I am newbie here. I need your help. I am trying to develop a tool where I want to implement multi-threading in vb.net. I am stuck in a place which I could not resolve. Let me explain you.
I have created a custom class as below:
Public Class TestClass
'Few private Members of my class
Private a As String,b As String
Public Structure abc
Dim xx as object
Dim yy as object
Dim zz as object
End Structure
Public aa(10) As abc
'Now I needed to override the constructor as the initialization of
'instance of this class can be made in two different ways due to
'requirement
Public Sub New(ByVal xy As String,ByRef yz As Object)
'Some internal method to initialize the object
Me.a=xy
Me.b=xy
End Sub
'Another way to create the instance
Public Sub New(ByVal xy As String,ByVal yz As String)
'Some internal method to initialize the object
Me.a=xy
Me.b=yz
End Sub
'Now a public method which I want to call using threading
Public Sub TestSub()
'Do something with Me.a,Me.b and aa(some index)
End Sub
End Class
Now in a different module I am creating the instance of this class as below:
Dim X1 As New TestClass("Some String",<Some Object reference>)
Dim X2 As New TestClass("Some String","Some String")
Now I have declared a sub in the same module like below
Sub DoMyStuff(ByRef A1 As TestClass)
A1.TestSub()
End Sub
After all This I want to create a thread and run the sub "DoMyStuff" by Passing a reference of X1
To Do this I have Imported System Threading:
Imports System.Threading
Inside Module after initialization of X1 and X2 I have written:
Dim T1 as New Threading.Thread(AddressOf DoMyStuff)
T1.Start(X1)
Here I am getting error: Overload Resolution Failed because no 'New' can be called with this arguments: 'Public Sub New(Start As System.Threading.ParameterizedThreadStart, maxStackSize As Integer)': Method 'Public Sub DoMyStuff(ByRef A1 As TestClass)' does not have a signature compatible with delegate 'Delegate Sub ParameterizedThreadStart(obj As Object)'. 'Public Sub New(Start As System.Threading.ThreadStart, maxStackSize As Integer)': Method 'Public Sub DoMyStuff(ByRef A1 As TestClass)' does not have a signature compatible with delegate 'Delegate Sub ThreadStart()'.
If I am writing like this,
Dim T1 As Threading.Thread
T1 = (AddressOf DoMyStuff)
T1.Start(X1)
I am getting the error: 'AddressOf' expression can not be converted to 'System.Threading.Thread' because 'System.Threading.Thread' is not a delegate type.
Might be I am missing some thing, which I am unable to find. I am not so much good in Delegate type concept. Also this is my first project with implementation of Threading. Looked for answers in Google but unable to understand to sort out. Your help highly solicited.
Thanks
Edit:
I have checked your reference Michael Z. and its really helpful. First of all a big Thank you for that :)
However, I am unable to understand a thing in there. If you help me out there, I would be highly grateful. As per my understanding, I have changed my code as below:
Public Delegate Sub Test2(ByRef A1 as TestClass)
Public T1 as Test2 = New Test2(AddressOf DoMyStuff)
Public Sub DoMyStuff(ByRef A1 as TestClass)
A1.TestSub()
End Sub
Now I have Declared in my module as follows:
Dim T as Threading.Thread = New Threading.Thread(AddressOf DoStuff)
Here I am unable to understand how to write the DoStuff sub as in your example you were working with TextBox1 which is an object in UI, so you wrote:
Public Sub DoStuff()
If TextBox1.InvokeRequired Then
TextBox1.Invoke(T1,"Hello")
End If
End Sub
But here I need to work with an object which is custom made i.e. X1 or X2. Also I need to pass the reference of the object in the Thread so that the method TestSub can work with the created object either X1 or X2. Totally lost here :(
Please correct me if anywhere I am wrong to understand your reference, otherwise if correctly understood, can you Please help me out here. Appreciate your help in advance.
Thanks
Firstly, ensure you're calling this from a method:
Dim T1 as New Threading.Thread(AddressOf DoMyStuff)
T1.Start(X1)
Next remove the ByRef in the method parameter like so Sub DoMyStuff(A1 As TestClass)
Essentially make your second Module look like:
Module Module2
Dim X1 As New TestClass("Some String", "asd")
Dim X2 As New TestClass("Some String", "Some String")
Sub TestCall
Dim T1 as New Thread(AddressOf DoMyStuff)
T1.Start(X1)
End Sub
Private Sub DoMyStuff(A1 As TestClass)
A1.TestSub()
End Sub
End Module
And you can call it like so:
TestCall()
Try using a simple Call method;
Call New Threading.Thread(AddressOf DoMyStuff).Start(X1)
Looking at what you wanting, there seems no reason to assign this to a variable?
Also - remember, when working with multiple contructors, you need to ensure the [TYPES] you pass in are correct and expected (this could be why your current process isn't working).
See this link for a similar issue.

How to remove all event handlers from an event?

I have the following class
Public Class SimpleClass
Public Event SimpleEvent()
Public Sub SimpleMethod()
RaiseEvent SimpleEvent()
End Sub
End Class
I instanciate it like
Obj = New SimpleClass
AddHandler Obj.SimpleEvent, Sub()
MsgBox("Hi !")
End Sub
And i'm trying to remove the event handler dynamically-created using the code in : Code Project
(I assume a complex application where it's difficult to use : RemoveHandler Obj.Event, AddressOf Me.EventHandler)
In their code there is the following method
Private Shared Sub BuildEventFields(t As Type, lst As List(Of FieldInfo))
For Each ei As EventInfo In t.GetEvents(AllBindings)
Dim dt As Type = ei.DeclaringType
Dim fi As FieldInfo = dt.GetField(ei.Name, AllBindings)
If fi IsNot Nothing Then
lst.Add(fi)
End If
Next
End Sub
But when calling this code using my object type, the next line returns nothing
Dim fi As FieldInfo = dt.GetField(ei.Name, AllBindings)
means that somehow my event is not recognized as a field.
Does anyone know how to remove all event handlers from an event ?
Cheers in advance.
It is the lambda expression that is getting you into trouble here. Don't dig a deeper hole, just use AddressOf and a private method instead so you can trivially use the RemoveHandler statement.
If you absolutely have to then keep in mind that the VB.NET compiler auto-generates a backing store field for the event with the same name as the event with "Event" appended. Which makes this code work:
Dim Obj = New SimpleClass
AddHandler Obj.SimpleEvent, Sub()
MsgBox("Hi !")
End Sub
Dim fi = GetType(SimpleClass).GetField("SimpleEventEvent", BindingFlags.NonPublic Or BindingFlags.Instance)
fi.SetValue(Obj, Nothing)
Obj.SimpleMethod() '' No message box
I'll reiterate that you should not do this.

VB.net delegate param array with invoke

I have this code in my Parser and I want to pass text to Form1 so I can update some Labels or whatever.
(My structure is as follows: Form1 -> Engine -> Parser)
Sometimes I need to pass 2 strings, sometimes more.
Public Class Parser
Public Event NewInfo(<[ParamArray]()> Byval strArray() as String)
Public Sub SomeParser(ByVal m As Match)
Dim strArray() As String = {"Word1", "Word2"}
RaiseEvent NewInfo(strArray)
End Sub
End Class
Then I have this another class. I pass the Array to Engine and after that, to Form1, finally:
Public Class Engine
Private parent as Form1
Private WithEvents Parser As New Parser
Private Sub New(ByRef parent as Form1)
Me.parent = parent
EndSub
Private Sub ChangeLabel(ByVal str() As String) Handles Parser.NewInfo
parent.UpdateTextLabel(str)
End Sub
And then I have this in Form1:
Public Class Form1
Private WithEvents Engine as New Engine(Me)
Public Delegate Sub UpdateTextLabelDelegate(<[ParamArray]()> ByVal text() As String)
Public Sub UpdateTextLabel(ByVal ParamArray str() As String)
If Me.InvokeRequired Then
Me.Invoke(New UpdateTextLabelDelegate(AddressOf UpdateTextLabel), str())
Else
(Do stuff here)
End Sub
End Class
The code stops at Me.invoke(New UpdateTextLabelDelegate).... -line.
Exception is something like: System.Reflection.TargetParameterCountException
So it means something like wrong amount of parameters.. How to do this properly?
I would be very pleased if someone could explain and if I could understand how to do this.
I don't think you need <[ParamArray]()> in your code since it's already an array that you are passing:
Public Delegate Sub UpdateTextLabelDelegate(ByVal text() As String)
And as far as passing the data through the invoke, don't use str(), just str
Public Sub UpdateTextLabel(ByVal str() As String)
If Me.InvokeRequired Then
Me.Invoke(New UpdateTextLabelDelegate(AddressOf UpdateTextLabel), str)
Else
'(Do stuff here)
End If
End Sub
Finally managed to solve this problem. It wasn't that difficult but my mistake was something inside my own head.
I made no changes to Parser.vb so the above code is Ok.
Also, no changes to Engine.vb.
Changes to Form1.vb are here:
Public Class Form1
Private WithEvents Engine as New Engine(Me)
Public Delegate Sub UpdateTextLabelDelegate(<[ParamArray]()> ByVal text() As String)
Public Sub UpdateTextLabel(ByVal str() As String)
If Me.InvokeRequired Then
Me.Invoke(New UpdateTextLabelDelegate(AddressOf UpdateTextLabel), New Object() {str})
Else
'(Do stuff here)
End Sub
End Class
So, all I did was to insert New Object() {args} to the invoke line and removed ParamArray from the Public Sub UpdateTextLabel -line..
But thanks for Kicking my head so I had the reason to go forward! :)

AdressOf method signature

Since I can't put in parameters, how can I respect the following signature?
Private Sub SetFocusToRow(ByRef ultraGridRow As Infragistics.Win.UltraWinGrid.UltraGridRow)
grdSoldeOuverture.ActiveCell = ultraGridRow.Cells(0)
grdSoldeOuverture.PerformAction(Infragistics.Win.UltraWinGrid.UltraGridAction.EnterEditMode)
End Sub
When I call it like this
Me.BeginInvoke(New MethodInvoker(AddressOf Me.SetFocusToTemplateAddRow))
I'm on .NET 2.0 in Visual Studio 2005 with Microsoft Visual Basic 2005 so a lambda expression is not an option.
You can use a lambda to capture the requirements and pass them in:
Foo arg = GetTheFoo()
BeginInvoke(New MethodInvoker(Sub() SetFoo(arg)))
Edit:
First, change your method to not pass ByRef - this is unnecessary:
Private Sub SetFocusToRow(ByVal ultraGridRow As Infragistics.Win.UltraWinGrid.UltraGridRow)
grdSoldeOuverture.ActiveCell = ultraGridRow.Cells(0)
grdSoldeOuverture.PerformAction(Infragistics.Win.UltraWinGrid.UltraGridAction.EnterEditMode)
End Sub
Next, define a delegate:
' Define your delegate:
Delegate Sub SetFocusToRowDelegate(ByVal ultraGridRow As Infragistics.Win.UltraWinGrid.UltraGridRow)
Then you can call via:
BeginInvoke(new SetFocusToRowDelegate(AddressOf SetFocusToRow), arg)
Since lambdas cause a problem you could try implementing them manually using an object:
Class FooCurry
Private bar as Foo
Private Sub new (foo as Foo)
bar = foo
End Sub
Public sub DoFoo()
bar.SetFoo()
EndSub
End Class
dim foocurry as new FooCurry(foo)
BeginInvoke(New MethodInvoker(AdressOf foocurry.DoFoo))
This is how lambdas are implemented under the hood, so this should work. You could generalise the object to take a delegate and use it in more places.

How to pass multiple parameters in thread in VB

I'm looking to pass two or more parameters to a thread in VB 2008.
The following method (modified) works fine without parameters, and my status bar gets updated very cool-y.
But I can't seem to make it work with one, two or more parameters.
This is the pseudo code of what I'm thinking should happen when the button is pressed:
Private Sub Btn_Click()
Dim evaluator As New Thread(AddressOf Me.testthread(goodList, 1))
evaluator.Start()
Exit Sub
This is the testthread method:
Private Sub testthread(ByRef goodList As List(Of OneItem), ByVal coolvalue As Integer)
StatusProgressBar.Maximum = 100000
While (coolvalue < 100000)
coolvalue = coolvalue + 1
StatusProgressBar.Value = coolvalue
lblPercent.Text = coolvalue & "%"
Me.StatusProgressBar.Refresh()
End While
End Sub
First of all: AddressOf just gets the delegate to a function - you cannot specify anything else (i.e. capture any variables).
Now, you can start up a thread in two possible ways.
Pass an Action in the constructor and just Start() the thread.
Pass a ParameterizedThreadStart and forward one extra object argument to the method pointed to when calling .Start(parameter)
I consider the latter option an anachronism from pre-generic, pre-lambda times - which have ended at the latest with VB10.
You could use that crude method and create a list or structure which you pass to your threading code as this single object parameter, but since we now have closures, you can just create the thread on an anonymous Sub that knows all necessary variables by itself (which is work performed for you by the compiler).
Soo ...
Dim Evaluator = New Thread(Sub() Me.TestThread(goodList, 1))
It's really just that ;)
Something like this (I'm not a VB programmer)
Public Class MyParameters
public Name As String
public Number As Integer
End Class
newThread as thread = new Thread( AddressOf DoWork)
Dim parameters As New MyParameters
parameters.Name = "Arne"
newThread.Start(parameters);
public shared sub DoWork(byval data as object)
{
dim parameters = CType(data, Parameters)
}
Dim evaluator As New Thread(Sub() Me.testthread(goodList, 1))
With evaluator
.IsBackground = True ' not necessary...
.Start()
End With
Well, the straightforward method is to create an appropriate class/structure which holds all your parameter values and pass that to the thread.
Another solution in VB10 is to use the fact that lambdas create a closure, which basically means the compiler doing the above automatically for you:
Dim evaluator As New Thread(Sub()
testthread(goodList, 1)
End Sub)
In addition to what Dario stated about the Delegates you could execute a delegate with several parameters:
Predefine your delegate:
Private Delegate Sub TestThreadDelegate(ByRef goodList As List(Of String), ByVal coolvalue As Integer)
Get a handle to the delegate, create parameters in an array, DynamicInvoke on the Delegate:
Dim tester As TestThreadDelegate = AddressOf Me.testthread
Dim params(1) As Object
params(0) = New List(Of String)
params(1) = 0
tester.DynamicInvoke(params)
Just create a class or structure that has two members, one List(Of OneItem) and the other Integer and send in an instance of that class.
Edit: Sorry, missed that you had problems with one parameter as well. Just look at Thread Constructor (ParameterizedThreadStart) and that page includes a simple sample.
Pass multiple parameter for VB.NET 3.5
Public Class MyWork
Public Structure thread_Data
Dim TCPIPAddr As String
Dim TCPIPPort As Integer
End Structure
Dim STthread_Data As thread_Data
STthread_Data.TCPIPAddr = "192.168.2.2"
STthread_Data.TCPIPPort = 80
Dim multiThread As Thread = New Thread(AddressOf testthread)
multiThread.SetApartmentState(ApartmentState.MTA)
multiThread.Start(STthread_Data)
Private Function testthread(ByVal STthread_Data As thread_Data)
Dim IPaddr as string = STthread_Data.TCPIPAddr
Dim IPport as integer = STthread_Data.TCPIPPort
'Your work'
End Function
End Class
I think this will help you...
Creating Threads and Passing Data at Start Time!
Imports System.Threading
' The ThreadWithState class contains the information needed for
' a task, and the method that executes the task.
Public Class ThreadWithState
' State information used in the task.
Private boilerplate As String
Private value As Integer
' The constructor obtains the state information.
Public Sub New(text As String, number As Integer)
boilerplate = text
value = number
End Sub
' The thread procedure performs the task, such as formatting
' and printing a document.
Public Sub ThreadProc()
Console.WriteLine(boilerplate, value)
End Sub
End Class
' Entry point for the example.
'
Public Class Example
Public Shared Sub Main()
' Supply the state information required by the task.
Dim tws As New ThreadWithState( _
"This report displays the number {0}.", 42)
' Create a thread to execute the task, and then
' start the thread.
Dim t As New Thread(New ThreadStart(AddressOf tws.ThreadProc))
t.Start()
Console.WriteLine("Main thread does some work, then waits.")
t.Join()
Console.WriteLine( _
"Independent task has completed main thread ends.")
End Sub
End Class
' The example displays the following output:
' Main thread does some work, then waits.
' This report displays the number 42.
' Independent task has completed; main thread ends.
With VB 14, you can do the following with Tuples:
Shared Sub _runner(data as (goodList As List(Of OneItem), coolvalue As Integer))
Console.WriteLine($"goodList: {data.goodList}")
Console.WriteLine($"coolvalue: {data.coolvalue}")
' do stuff...
End Sub
Dim thr As New Thread(AddressOf _runner)
thr.Start((myGoodList, cval))