Target parameter count exception on delegate Sub - vb.net

I have the following code to write some text from different functions and subs but that has been working ok but now I'm getting the target parameter count exception when I call the delegate from SerialPort DataReceived event.
I can't figure out what I'm doing wrong, Any ideas?
Delegate Sub PrintSmsLogDelegate(ByVal NewText As String, ByVal NewLine As Boolean)
Protected Friend Sub PrintSmsLog(ByVal NewText As String, Optional ByVal NewLine As Boolean = True)
If Me.InvokeRequired Then
Dim Txt As New PrintSmsLogDelegate(AddressOf PrintSmsLog)
'Me.Invoke(Txt, NewText)'This fail too
Me.Invoke(Txt, New Object() {NewText}) '<--- TargetParameterCountException
Else
'...
End If
End Sub
Private Sub SmsSerialPort_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SmsSerialPort.DataReceived
'... code to receive data and save it in "Lines" variable
Dim Lines as String
Me.PrintSmsLog(Lines, False)
End Sub

The problem is that your PrintSmsLogDelegate delegate declaration contains 2 required parameter.
So you have to provide the 2nd parameter as well.
The method signature for Invoke method is this:
Function Control.Invoke(method As [Delegate], ParamArray args As Object()) As Object
So you should call your PrintSmsLogDelegate delegate instance (which is Txt) with two parameters even if the PrintSmsLog method does not require the 2nd parameter.
Me.Invoke(Txt, NewText, True)
You cannot call the Invoke method with a single array parameter. Due to ParamArray keyword an array will be automatically created of the multiple parameter you specify.

Related

Can you pass a delegate function as an optional parameter?

I know that in Visual Basic, delegate function cannot contain optional parameters. But can a method take a delegate as an optional parameter?
What I want to do is this:
Delegate Sub MyDelegate(ByVal input As String)
Sub MyDelegateDefault(ByVal input As String)
'by default do nothing'
End Sub
Sub MyDelegateCustom1(ByVal input As String)
'do something here'
End Sub
In a different part of code:
Sub OtherFunction(ByVal str As String, Optional ByVal delegate As MyDelegate = AddressOf MyDelegateDefault)
delegate(str)
End Sub
Sub ParentFunction()
OtherFunction("", ) '< "" as string, nothing for optional delegate parameter'
End Sub
Note how the final function OtherFunction takes a optional delegate as second parameter.
Is this a thing? Can a delegate function be an optional parameter?
A parameter that is of a reference type can only be defaulted to null. Change the default value to null, check for the null condition, and don't call the delegate (do nothing).

VB.NET: Sending Multiple Arguments To a Background Worker

I am trying to use a BackgroundWorker to perform tasks on a separate thread.
I am able to pass a single argument to the BackgroundWorker as below:
Send argument to BackgroundWorker:
Private Sub btnPerformTasks_Click(sender As System.Object, e As System.EventArgs) Handles btnPerformTasks.Click
Dim strMyArgument as String = "Test"
BW1.RunWorkerAsync(strMyArgument)
End Sub
Retrieve argument inside BackgroundWorker:
Private Sub BW1_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles BW1.DoWork
Dim strMyValue As String
strMyValue = e.Argument 'Test
End Sub
There are only 2 overloaded methods for RunWorkerAsync(). One that takes no arguments and one that takes one argument.
I want to know:
How can I pass multiple values to BW1.RunWorkerAsync()
How can I retrieve these multiple values from inside BW1_DoWork
You can wrap your arguments in an object and then pass that object to the worker.
To retrieve it, you can just cast e in the DoWork to your custom type.
here's an example:
' Define a class named WorkerArgs with all the values you want to pass to the worker.
Public Class WorkerArgs
Public Something As String
Public SomethingElse As String
End Class
Dim myWrapper As WorkerArgs = New WorkerArgs()
' Fill myWrapper with the values you want to pass
BW1.RunWorkerAsync(myWrapper)
' Retrieve the values
Private Sub bgw1_DoWork(sender As Object, e As DoWorkEventArgs)
' Access variables through e
Dim args As WorkerArgs = e.Argument
' Do something with args
End Sub

Invoke wont invoke?

I'm doing this:
Delegate Sub SetTextBoxText_Delegate(ByVal [Label] As TextBox, ByVal [text] As String)
' The delegates subroutine.
Public Sub SetTextBoxText_ThreadSafe(ByVal [Label] As TextBox, ByVal [text] As String)
' InvokeRequired required compares the thread ID of the calling thread to the thread ID of the creating thread.
' If these threads are different, it returns true.
If [Label].InvokeRequired Then
MsgBox("invoke")
Dim MyDelegate As New SetTextBoxText_Delegate(AddressOf SetTextBoxText_ThreadSafe)
Me.Invoke(MyDelegate, New Object() {[Label], [text]})
Else
MsgBox("noinvoke")
[Label].Text = [text]
End If
End Sub
However it always uses noinvoke. If I try setting it normaly it gives me a thread-safe warning and doesn't work. If I force invoke then it says the control isn't created?
Could someone help?
It's most likely because the control has not yet been created when you try to access it. Wait until the control has loaded, or check it using Label.Created. Like so:
Public Sub SetTextBoxText_ThreadSafe(ByVal Label As TextBox, ByVal text As String)
If Label.Created Then
If Label.InvokeRequired Then
MsgBox("invoke")
Dim MyDelegate As New SetTextBoxText_Delegate(AddressOf SetTextBoxText_ThreadSafe)
Me.Invoke(MyDelegate, New Object() {Label, text})
Else
MsgBox("noinvoke")
Label.Text = text
End If
End If
End Sub
P.S. You don't need a custom delegate type, just use Action(Of TextBox, String). You also don't need square brackets around Label or text.

Trying to understand Control.BeginInvoke code over at MSDN

I am referring to this document on MSDN. I understand what ".BeginInvoke" does, however looking at the example code on the document
Delegate Sub MyDelegate(myControl As Label, myArg2 As String)
Private Sub Button_Click(sender As Object, e As EventArgs)
Dim myArray(1) As Object
myArray(0) = New Label()
myArray(1) = "Enter a Value"
myTextBox.BeginInvoke(New MyDelegate(AddressOf DelegateMethod), myArray)
End Sub 'Button_Click
Public Sub DelegateMethod(myControl As Label, myCaption As String)
myControl.Location = New Point(16, 16)
myControl.Size = New Size(80, 25)
myControl.Text = myCaption
Me.Controls.Add(myControl)
End Sub 'DelegateMethod
The delegate myDelegate (and the DelegateMethod) accepts a control and a string, but, at the .BeginInvoke, a Label control is passed and an array...
myTextBox.BeginInvoke(New MyDelegate(AddressOf DelegateMethod), myArray)
and in the "DelegateMethod" there is
myControl.Text = myCaption
Shouldn't a string be passed instead of the array? Am I missing something?
BeginInvoke can accept two parameters. One is a delegate, in this case AddressOf DelegateMethod.
The other parameter is an array of parameters. DelegateMethod accepts two parameters: a label and a string. In order to pass these using begininvoke, an array of objects with two members is passed in to beinginvoke to match the parameters of the method: a label and a string.
So both the label and the string are passed in using this array
Your code is correct. The framework casts the parameters appropriately from the object array on your behalf.

Using private function

Hi
I am using following code to run a private function.
I have two values in my combo box, One and Two and two private functions with the same names, Private Sub One() and Private Sub Two()
I want my application to call the function whatever value user choses in the combo box.
If One is chosen in the combo box, Private function one should be called.
Thanks
Code is below, that does not work
Public Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim vrValue = ComboBox1.Items(1)
Call vrValue()' In this case vrValue is Two, so Two() should be called.
End Sub
Private Sub two()
MsgBox("Function called")
End Sub
Make your subs functions (the only difference is the returning of a value) and put them in their own class:
Public Class RunFunctions
Dim oMessageBox As MessageBox
Public Function One() As String
'oMessageBox = MessageBox
Return "Message One"
End Function
Public Function Two() As String
Return "Message Two"
End Function
End Class
Add Each function from the class as an item in your combo box:
Public Class Combo_Functions
Dim oRunFunction As RunFunctions
Private Sub ComboBox1_SelectedIndexChanged(ByVal sender As Object _
, ByVal e As System.EventArgs) Handles ComboBox1.SelectedIndexChanged
MessageBox.Show(ComboBox1.Items(ComboBox1.SelectedIndex()))
End Sub
Private Sub Combo_Functions_Load(ByVal sender As Object _
, ByVal e As System.EventArgs) Handles Me.Load
oRunFunction = New RunFunctions
ComboBox1.Items.Add(oRunFunction.One())
ComboBox1.Items.Add(oRunFunction.Two())
End Sub
End Class
When the combo box is changed (or use the code for the button click) the messagebox for the correct function is executed.
Dim vrValue = ComboBox1.SelectedItem.ToString()
Select vrValue
Case "One"
One()
Else
Two()
End Select
It looks like what you're trying to do is to dynamically call a particular method using a string variable that contains its name. For example, the combo box would contain items "One" and "Two", and you would call the sub named "One" if the first item in the combo box is selected, or the sub named "Two" if the second item is selected. To that end, you may find this article interesting:
http://www.codeproject.com/KB/cs/CallMethodNameInString.aspx
The code in the article is in C#, which shouldn't be too difficult to convert to VB. But here's the translated version of the code for simply invoking a method without passing or returning any parameters (note: I have not tested this code). It simply uses reflection to find the appropriate method:
Public Shared Sub InvokeStringMethod(ByVal typeName As String, ByVal methodName As String)
'Get the type of the class
Dim calledType As Type = Type.[GetType](typeName)
'Invoke the method itself
calledType.InvokeMember(methodName, BindingFlags.InvokeMethod Or BindingFlags.[Public] Or BindingFlags.[Static], Nothing, Nothing, Nothing)
End Sub
You simply pass the name of the class that contains the method(s) you want to call as the typeName and the name of the method itself that you want to call as the methodName:
InvokeStringMethod("MyClass", "Two")