Using Delegates in vb.net - vb.net

I'm new to vb.net and trying to build a win CE app in vb.net. The sdk of the handheld device is in C# which I converted with an online converter to vb.net.
Below is the C# code:
public class DecodeEventArgs : EventArgs
{
private string barcode;
private byte type;
public DecodeEventArgs(string barcodeData, byte typeData)
{
barcode = barcodeData;
type = typeData;
}
public string Barcode
{
get { return barcode; }
set { barcode = value; }
}
public byte Type
{
get { return type; }
set { type = value; }
}
}
Converted to vb.net as:
Public Class DecodeEventArgs
Inherits EventArgs
Public barcode As String
Public type As Byte
Public Sub New(ByVal barcodeData As String, ByVal typeData As Byte)
barcode = barcodeData
type = typeData
End Sub
Public Property pBarcode() As String
Get
Return barcode
End Get
Set(ByVal value As String)
barcode = value
End Set
End Property
Public Property pType() As Byte
Get
Return type
End Get
Set(ByVal value As Byte)
type = value
End Set
End Property
End Class
In my conversion from C# I added a 'p' to the name of the Properties as you see in my vb.net code because I had a error that said
Barcode is already declared as a public string in this class
I'm not sure if that is part of my problem but my real issue is, on a form they used .BeginInvoke to call the class with this code:
void scanner_DecodeEvent(object sender, DecodeEventArgs e)
{
Win32.sndPlaySound(Properties.Resources.Scan, Win32.SND_ASYNC | Win32.SND_MEMORY);
this.BeginInvoke((Action<string>)delegate(string barcode)
{
scanCount = 0;
ListViewItem item = new ListViewItem(new string[] { barcode });
lstView.Items.Insert(0, item);
}, e.Barcode);
}
Which I converted to vb.net as:
Private Sub scanner_DecodeEvent(ByVal sender As Object, ByVal e As DecodeEventArgs)
PlaySound()
Me.BeginInvoke(DirectCast(Barcode As String) )
scanCount = 0
Dim item As New ListViewItem(New String() {barcode})
lstView.Items.Insert(0, item)
End Sub
Which gives me an error about Barcode not being declared. This is where I'm stuck. Thanks for your assistance in advance

That C# snippet creates an anonymous method which it invokes to perform the actions on the UI thread, and it sends e.Barcode as a parameter. Your VB.NET conversion only tries to invoke some strange, and incomplete, use of DirectCast. DirectCast is not needed in the VB.NET conversion as you don't have any delegate keyword which you must cast to a delegate method.
Your most simple solution would be to use a Lambda method:
Me.BeginInvoke(Sub() 'Lambda anonymous method.
scanCount = 0
Dim item As New ListViewItem(New String() {e.Barcode})
lstView.Items.Insert(0, item)
End Sub)
EDIT:
Since you get errors when using the lambda expression I assume you target .NET Framework 3.5 or lower. For that matter it gets a bit more complicated as you must now put the code in a different method:
Private Sub AddBarcode(ByVal Barcode As String)
scanCount = 0
Dim item As New ListViewItem(New String() {Barcode})
lstView.Items.Insert(0, item)
End Sub
Then you must declare your own delegate method which you can use to perform the invocation:
Delegate Sub AddBarcodeDelegate(ByVal Barcode As String)
Private Sub scanner_DecodeEvent(ByVal sender As Object, ByVal e As DecodeEventArgs)
Me.BeginInvoke(New AddBarcodeDelegate(AddressOf AddBarcode), e.Barcode)
End Sub

Related

How to return matching value in textbox?

I have a combo box that is populated by a csv. In my csv, I have 3 columns, the first column is id #s.
Maybe you can try ValueMember.
This is an example :
public partial class Form1 : Form
{
List<Student> listStudents = new List<Student>();
public Form1()
{
InitializeComponent();
listStudents.Add(new Student(101, "name1", "male"));
listStudents.Add(new Student(102, "name2", "female"));
listStudents.Add(new Student(103, "name3", "female"));
listStudents.Add(new Student(104, "name4", "male"));
var maleStudentList = listStudents.Where(student => student.gender == "male").ToList();
comboBox1.DisplayMember = "name";
comboBox1.ValueMember = "id";
comboBox1.DataSource = maleStudentList;
}
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
label1.Text = ((Student)comboBox1.SelectedItem).id.ToString();
}
}
class Student
{
public int id { get; set; }
public string name { get; set; }
public string gender { get; set; }
public Student(int id, String name, String gender)
{
this.id = id;
this.name = name;
this.gender = gender;
}
}
Source : https://social.msdn.microsoft.com/Forums/windowsmobile/en-US/1a978579-1938-44cd-aab3-d1964548a814/why-dont-comboboxs-item-have-a-tag-property?forum=csharpgeneral
What I would do is create a class to represent a row in the CSV file and have your properties represent the columns. The reason for this is because you could then convert the CSV file to a list of your class and then bind the ComboBox to the list.
When you bind your ComboBox you will set the DisplayMember to the Description and the ValueMember to the Id.
I'm not sure how you're reading the CSV file nor do I know the name of your second column, so I'm making some assumptions in my example, but it should plenty to go off of. Please keep in mind, that there are likely better ways of parsing CSV files, this is just an quick example to demonstrate how the binding would work.
This is how you'd define your class:
Public Class CsvRow
Public Property Id As Integer
Public Property Column2 As String
Public Property Description As String
End Class
This is how you'd read the CSV and bind the objects.
Dim lines() As String = IO.File.ReadAllLines("my-file.csv")
Dim rows As New List(Of CvsRow)()
For Each line In lines
Dim cells() As String = line.Split(","c)
Dim row As New CvsRow()
If (cells.Length = 3 AndAlso Integer.TryParse(cells(0).Trim(), row.Id) Then
row.Column2 = cells(1)
row.Description = cells(2)
rows.Add(row)
End If
Next
Dim binder As New BindingSource()
binder.DataSource = rows
ComboBoxDescription.DataSource = binder.DataSource
ComboBoxDescription.DisplayMember = "Description"
ComboBoxDescription.ValueMember = "Id"
Now in the ComboBox's SelectedValueChanged you can get the SelectedItem and convert it back to a CsvRow to get the properties:
Private Sub ComboBoxDescription_SelectedValueChanged(sender As Object, e As EventArgs) Handles ComboBoxDescription.SelectedValueChanged
If (ComboBoxDescription.SelectedItem IsNot Nothing) Then
Dim row As CvsRow = DirectCast(ComboBoxDescription.SelectedItem, CvsRow)
'row.Id and row.Description are now available
End If
End Sub
I made a class called Product with 3 properties. I guessed that the middle column would be the name of the product. When you add an object to a combo box, the combo will call .ToString on the object to get what to display. I provided an override of the .ToString method to tell the combo to display the name property of the Product object. Finally, I replaced the default constructor with a parameterized one to make creating the Product with all its properties easier.
In the FillCombo method (probably called from the Form.Load) I read your data file. You can change that to however you are reading the file. The important part is where you call the New method of the product adding each of the properties from your file. The entire Property object will all its properties are added to the combo box.
The SelectedIndexChanged event shows you how to retrieve the properties and display them wherever you want. To the combo box, it is an Object. You must cast it back to a Product to be able to access its properties.
Public Class Product
Public Property ID As Integer
Public Property Name As String
Public Property Description As String
Public Overrides Function ToString() As String
Return Name
End Function
Public Sub New(iID As Integer, sName As String, sDesc As String)
ID = iID
Name = sName
Description = sDesc
End Sub
End Class
Private Sub FillCombo()
Dim lines = File.ReadAllLines("data.csv")
For Each line In lines
Dim props = line.Split(","c)
Dim p As New Product(CInt(props(0)), props(1), props(2))
ComboBox1.Items.Add(p)
Next
End Sub
Private Sub ComboBox1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ComboBox1.SelectedIndexChanged
Dim p = DirectCast(ComboBox1.SelectedItem, Product)
TextBox1.Text = p.ID.ToString
TextBox2.Text = p.Description
End Sub

Create Custom Class Dynamically

I am working on a project where I need to create a multitude of custom classes to interact properly with an API (While I know there might be questions on why, and such, but the short is it has to be this way).
Is there a way to create a complete custom class dynamically on the fly? So instead of
class person
Private _Height
Property Height As Integer
Get
Return _Height
End Get
Set(value As Integer)
_Height = value
End Set
End Property
'Continue for all properties of person
I would like to be able to create a new object and through other input create this dynamically.
dim NewClass as object
dim NewProperty as property
NewProperty.name="Height"
NewProperty.datatype=string
NewClass.AddProperty(NewProperty)
Is this possible? It would save me a lot of time if it is.
I don't like late binding but there are options (I like my option strict on). Like using the DynamicObject or the ExpandoObject class. Your question is vague so I have no idea if it can work.
Sub Main()
Dim test As Object = New SampleDynamicClass()
test.SomeProperty = "123"
Console.WriteLine(test.SomeProperty)
Console.ReadLine()
End Sub
Public Class SampleDynamicClass
Inherits DynamicObject
Private _values As New Dictionary(Of String, String)
Public Sub New()
End Sub
Public Function GetPropertyValue(ByVal propertyName As String) As String
Return _values(propertyName)
End Function
Public Function SetPropertyValue(ByVal propertyName As String, ByVal value As Object) As Boolean
If _values.ContainsKey(propertyName) Then
_values(propertyName) = value.ToString()
Else
_values.Add(propertyName, value.ToString())
End If
Return True
End Function
Public Overrides Function TryGetMember(ByVal binder As GetMemberBinder,
ByRef result As Object) As Boolean
result = GetPropertyValue(binder.Name)
Return If(result Is Nothing, False, True)
End Function
Public Overrides Function TryInvokeMember(ByVal binder As InvokeMemberBinder,
ByVal args() As Object,
ByRef result As Object) As Boolean
result = GetPropertyValue(binder.Name)
Return If(result Is Nothing, False, True)
End Function
Public Overrides Function TrySetMember(binder As SetMemberBinder, value As Object) As Boolean
Return SetPropertyValue(binder.Name, value)
End Function
Dim person = New With {Key .Height = 12}
Dim personTypes = New With {Key .Happy = 1, .Sad = 2}
Dim personsAndTypes = New With {Key .Person = person, .Type = personTypes}
The question is kind of vague, but if you have no need for other fields and methods, or reuse Anonymous Types

how to call a method with parameter Action(Of T) with variable

I am in the process of converting some c# code to vb.net and I keep running into an issue with a particular method.
Here is the c# method signature -
public void DoSomething(Action<T> something)
{ .... do something in here }
Here is my vb.net conversion of the signature -
Public Sub DoSomething(ByVal something As Action(Of T))
....do something in here
End Sub
I have to call this with a variable. Here is a sample c# call -
_myobject.DoSomething(x => { newValue = x.CallSomeMethod() });
How would I perform this same call using Vb.Net?
I've tried this (along with a few variations), but the newValue object is always empty -
_myObject.DoSomething(Sub(x) newValue = x.CallSomeMethod())
I've also tried this -
_myObject.DoSomething(Function(x) newValue = x.CallSomeMethod() End Function)
If I do this -
_myObject.DoSomething(Function(x) newValue = x.CallSomeMethod())
I get an error message stating Cannot apply operator '=' to operands of type myType and myType
The SourceClass has the DoSomething method and the TargetClass has the CallSomeMethod that will be called as part of the anonymous Sub:
Public Class SourceClass
Public Sub DoSomething(ByRef something As Action(Of TargetClass))
Dim t As New TargetClass
something(t)
End Sub
End Class
Public Class TargetClass
Function CallSomeMethod() As Integer
Return 1000
End Function
End Class
In the Main method add this:
Public Module Module1
Sub Main()
Dim newValue = 11
Dim myObject = New SourceClass
myObject.DoSomething(New Action(Of TargetClass)(Sub(obj) newValue = obj.CallSomeMethod()))
Debug.WriteLine(newValue)
End Sub
End Module
In this example newValue will be assigned 1000.

VB.NET: How can I have events return a value like I can in C#?

In C#, I can do this:
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
Class1 c1 = new Class1();
c1.OnNeedInt += new Class1.NeedInt(c1_OnNeedInt);
int i = c1.GetInt();
}
int c1_OnNeedInt()
{
return 1;
}
}
public class Class1
{
public delegate int NeedInt();
public event NeedInt OnNeedInt;
public int GetInt()
{
return OnNeedInt == null ? 0 : OnNeedInt();
}
}
Notice the line int i = c1.GetInt();. I can't seem to get VB.NET 4.0 to do something similiar. Any help?
I thinks its even easier than most people think...
Class MyClass
Public Event MyEvent(ByRef MyVariable as String)
Private Sub DoSomething()
Dim SomethingINeed as String = String.Empty
RaiseEvent MyEvent(SomethingINeed)
'SomethingINeed will now contain "Goodbye Cruel World"
End sub
End Class
Then in the class that monitors the event...
Class MyOtherClass
Private Sub New()
AddHandler MyClass.MyEvent, Addressof MyEventHandler
End Sub
Private Sub MyEventHandler(ByRef StringToPassBack as String)
StringToPassBack = "Goodbye Cruel World"
End Sub
End Class
It's all about the ByRef keywords in both the event declaration and the eventhandler sub.
That's not possible in vb.net, events must be raised with the RaiseEvent statement. It doesn't return a value. It is a pretty questionable practice anyway, an event can have zero or multiple subscribers. No telling what the return value might be. Just use a delegate instead:
Class Page
Public Sub New()
Dim obj As New Class1
Dim dlg As New Func(Of Integer)(AddressOf obj.GetInt)
Dim i As Integer = dlg()
End Sub
End Class
Class Class1
Public Function GetInt() As Integer
Return 42
End Function
End Class
In VB, you don't need to check to see if anyone is attached to your event handler. You can just call RaiseEvent and if anyone is listening to it, it will work. However, the event isn't intended to return a value. You could try sticking it into an event arg and pass that around, but that gets messy.
#HansPassant's solution is close, but not quite what you were asking for. Altering his solution a bit:
Delegate Function FetchIt() As Integer
Class Page
Public Sub New()
Dim obj As New Class1
Dim i As Integer = obj.GetInt(AddressOf c1_OnNeedInt)
End Sub
Function c1_OnNeedInt() As Integer
Return 42
End Function
End Class
Class Class1
Public Function GetInt(fetcher As FetchIt) As Integer
Return fetcher()
End Function
End Class
Alternatively, you could do this without the custom delegate using Lambda's:
Class Page
Public Sub New()
Dim obj As New Class1
Dim dlg As New Func(Of Integer)(AddressOf c1_OnNeedInt)
Dim i As Integer = obj.GetInt(dlg)
End Sub
Function c1_OnNeedInt() As Integer
Return 42
End Function
End Class
Class Class1
Public Function GetInt(fetcher As Func(Of Integer)) As Integer
Return fetcher()
End Function
End Class
I found an answer to my issue. In the base class that my ASP.NET user controls inherit, I have this:
Dim _Connection As MyConnection
Public Property Connection As MyConnection
Get
If _Connection Is Nothing Then
RaiseEvent OnNeedConnection(_Connection)
End If
Return _Connection
End Get
Set(value As MyConnection)
_Connection = value
End Set
End Property
Public Delegate Sub NeedConnection(ByRef Connection As MyConnection)
Public Event OnNeedConnection As NeedConnection
In my web form codebehind, I wire it up manually to this:
Sub ServeConnection(ByRef Connection As MyConnection)
Connection = oConn
End Sub
The actual connection is hosted on the webform's codebehind, but I have several user controls that need to use this connection. Any time any of the user controls need the connection, their base class requests it and the host page serves it. This is made possible by the ByRef keyword.
This is the closest C# equivalent I could put together.

VB.NET CheckedListBox Tag?

Is there a Tag for an Item in the CheckedListBox? Or something similar? I would like to be able to store and ID associated with the item that I'm displaying.
You don't need a Tag property. The control accepts any object, that means you don't have to put just strings in it. Make a class that has a string (and overrridden ToString()) and any other data members you need.
Public Class Form1
Protected Overrides Sub OnLoad(ByVal e As System.EventArgs)
MyBase.OnLoad(e)
CheckedListBox1.Items.Add(New MyListBoxItem() With {.Name = "One", .ExtraData = "extra 1"})
CheckedListBox1.Items.Add(New MyListBoxItem() With {.Name = "Two", .ExtraData = "extra 2"})
End Sub
Private Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Button1.Click
For Each obj As Object In CheckedListBox1.CheckedItems
Dim item As MyListBoxItem = CType(obj, MyListBoxItem)
MessageBox.Show(String.Format("{0}/{1} is checked.", item.Name, item.ExtraData))
Next
End Sub
End Class
Public Class MyListBoxItem
Private _name As String
Private _extraData As String
Public Property Name As String
Get
Return _name
End Get
Set(ByVal value As String)
_name = value
End Set
End Property
Public Property ExtraData As String
Get
Return _extraData
End Get
Set(ByVal value As String)
_extraData = value
End Set
End Property
Public Overrides Function ToString() As String
Return Name
End Function
End Class
(The overridden ToString() dictates what will be displayed in the box.)
You can inherit your own control from CheckedListBox and create a property, in C# it would be like this, the rest of the functionality remains the same as it is inherited so no further additional code required:
public class MyCheckedListbox : System.Windows.Forms.CheckedListBox{
private object thisObj;
public object Tag{
get{ return this.thisObj; }
set{ this.thisObj = value; }
}
}
Edit: Decided to include the VB.NET version for everyone's benefit also...
Public Class MyCheckedListBox Inherits System.Windows.Forms.CheckedListBox
Private thisObj As Object
Public Property Tag As Object
Get
Tag = thisObj
End Get
Set (objParam As Object)
thisObj = objParam
End Set
End Property
End Class
Of course, this is plain and uses boxing but works nicely...
Hope this helps
Translation of tommieb75 answer to VB.NET:
Public Class MyCheckedListbox
Inherits System.Windows.Forms.CheckedListBox
Private thisObj As Object
Public Property Tag() As Object
Get
Return Me.thisObj
End Get
Set(ByVal value As Object)
Me.thisObj = value
End Set
End Property
End Class
I use the translator at www.developerfusion.com/tools