testing objects for nothing and reflection - vb.net

I'm getting NullReferenceException on:
faxnum = Customer.ContactLink.Contact.DefaultFaxLink.Phone.PhoneNumber
The null ref is on the DefaultFaxLink. Since there isn't a fax number, the DefaultFaxLink isn't initialized and I know that if it were, I wouldn't get the error on the assignment.
So, my question is, is there a way I can trap the exception without having to test each object to see if it is nothing?
I just want to treat the entire right hand portion of a statement so that if any part is nothing, I just assign nothing to the left var.
Short of that, could I use reflection on the base object to evaluate each member and its sub-members and assign an empty value?

You could use a Try-Catch block for NullReferenceException
Public Class Customer
Public ContactLink As ContactLink
End Class
Public Class ContactLink
Public Contact As Contact
End Class
Public Class Contact
Public DefaultFaxLink As FaxLink
End Class
Public Class FaxLink
Public Phone As Phone
End Class
Public Class Phone
Public PhoneNumber As String
End Class
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim objCustomer As New Customer
objCustomer.ContactLink = New ContactLink
objCustomer.ContactLink.Contact = New Contact
objCustomer.ContactLink.Contact.DefaultFaxLink = New FaxLink
Dim PhoneNumber As String = ""
Try
PhoneNumber = objCustomer.ContactLink.Contact.DefaultFaxLink.Phone.PhoneNumber
Catch ex As NullReferenceException
PhoneNumber = ""
Catch ex As Exception
MsgBox(ex.Message)
End Try
If Not String.IsNullOrEmpty(PhoneNumber) Then
MsgBox("Fax number is..." & PhoneNumber)
Else
MsgBox("No fax number!")
End If
End Sub

Write a function.
Public Class Customer
Public Function GetFaxNumberSafe() As String
If Me.ContactLink IsNot Nothing AndAlso
Me.ContactLink.Contact IsNot Nothing AndAlso
Me.ContactLink.Contact.DefaultFaxLink IsNot Nothing AndAlso
Me.ContactLink.Contact.DefaultFaxLink.Phone IsNot Nothing Then
Return Customer.ContactLink.Contact.DefaultFaxLink.Phone.PhoneNumber
Else
Return Nothing
End If
End Function
End Class
You could also set up your objects to lazy-load instantiate on access so you always have an object reference.
Public Class Customer
Private _contactLink As New Lazy(Of ContactLink)()
Public ReadOnly Property ContactLink As ContactLink
Get
Return _contactLink.Value
End Get
End Property
End Class

Related

VB.net OOP How to call method in namespace from FrmMain

I am trying to make a VB.net WinForm-application that connects to a database.
I have a class with the specs to make a database-connection (Intersystems Cache). The database isn't important, the principile does.
How do I call the methods in this class form the FrmMain?
I can't get my head around it.
Thanks
Imports Test.NSConnection.Connection
Public Class FrmMain
Private Sub FrmMain_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'how do I call the DoOpenConnectionCache?
End Sub
End Class
and:
Imports InterSystems.Data.CacheClient
Imports InterSystems.Data.CacheTypes
Imports System
Imports System.Data
Namespace NSConnection
Public Class Connection
Private _cacheString As String
Public Property cacheString() As String
Get
Return _cacheString
End Get
Set(ByVal value As String)
_cacheString = value
End Set
End Property
Private _cnCache As CacheADOConnection
Public Property cnCache() As CacheADOConnection
Get
Return _cnCache
End Get
Set(ByVal value As CacheADOConnection)
_cnCache = value
End Set
End Property
Private Sub SetConnectionString()
Dim _cacheString As String = "Server = localhost; Port = ****; Namespace = ****; User ID= **** ; Password= ****;"
Dim _cnCache = New CacheConnection(_cacheString)
End Sub
Public Sub DoOpenConnectionCache()
Try
If _cnCache.State = ConnectionState.Closed Then
_cnCache.Open()
End If
Catch ex As Exception
MessageBox.Show("Error" & vbCrLf & ex.Message)
End Try
End Sub
Public Sub DoCloseConnectionCache()
Try
If _cnCache.State = ConnectionState.Open Then
_cnCache.Close()
End If
Catch ex As Exception
MessageBox.Show("Error" & vbCrLf & ex.Message)
End Try
End Sub
End Class
End Namespace
If you want to create an object of type Connection, then you can do it like this:
Dim Connection1 As Connection = New Connection()
and call its method DoOpenConnectionCache

Removing items in a collection based on listbox string

Having issues when clicking the remove button. If more of my code is needed, let me know. I get this error on the line AddressList.Remove(selectedName):
System.ArgumentException: 'Argument 'Key' is not a valid value.
I've tried many variations but can't figure out why this doesn't work. I think it has something to do with how the strings are concatenated in the listbox. I need to be able to remove entries from the collection and the listbox. Any help would be greatly appreciated.
Module EmailCollection
Public AddressList As New Collection
Public Sub AddRecord(ByVal a As cAddress)
Try
AddressList.Add(a)
Catch ex As Exception
MessageBox.Show("Error: inputs must be characters valid in string format")
End Try
End Sub
End Module
public class form1
Private Sub btnAdd_Click(sender As Object, e As EventArgs) Handles btnAdd.Click
Dim frmAdd As New AddNewName
frmAdd.ShowDialog()
UpdateListBox()
End Sub
Private Sub UpdateListBox()
lstAddress.Items.Clear()
Dim a As cAddress
For Each a In AddressList
lstAddress.Items.Add(String.Concat(a.strName, a.strEmail, a.strPhone, a.strComment))
Next
If lstAddress.Items.Count > 0 Then
lstAddress.SelectedIndex = 0
End If
End Sub
Private Sub btnRemove_Click(sender As Object, e As EventArgs) Handles btnRemove.Click
Dim selectedName As String
Try
' Get the selected value.
selectedName = lstAddress.SelectedItem.ToString()
' Remove the selected name from the list box and the collection.
If MessageBox.Show("Are you sure?",
"Confirm Deletion",
MessageBoxButtons.YesNo) = Windows.Forms.DialogResult.Yes Then
lstAddress.Items.Remove(selectedName)
AddressList.Remove(selectedName)
End If
Catch ex As NullReferenceException
MessageBox.Show("Select an item to remove.", "Selection Needed")
End Try
End Sub
end class
In your Module I changed AddressList from the old VB6 Collection type to the .net List(Of T). The T stands for Type.
Module EmailCollection
Public AddressList As New List(Of cAddress)
Public Sub AddRecord(ByVal a As cAddress)
AddressList.Add(a)
End Sub
End Module
I guessed that your cAddress class looks something like this. I added a custom .ToString method so the list box will display the data you wish but the item, itself, will be a cAddress object.
Public Class cAddress
Public Property strName As String
Public Property strEmail As String
Public Property strPhone As String
Public Property strComment As String
Public Overrides Function ToString() As String
Return $"{strName}, {strEmail}, {strPhone}, {strComment}"
End Function
End Class
In the Form...
Instead of adding a string to the list box I added the cAddress object. The list box calls .ToString on the object to get the display value.
Private Sub UpdateListBox()
ListBox1.Items.Clear()
For Each a As cAddress In AddressList
ListBox1.Items.Add(a)
Next
If ListBox1.Items.Count > 0 Then
ListBox1.SelectedIndex = 0
End If
End Sub
In the remove button I cast the selected item to its underlying type, cAddress. This is the item removed from the AddressList. Then simply remove the selected item from the list box.
Private Sub btnRemove_Click(sender As Object, e As EventArgs) Handles btnRemove.Click
If MessageBox.Show("Are you sure?",
"Confirm Deletion",
MessageBoxButtons.YesNo) = Windows.Forms.DialogResult.Yes Then
AddressList.Remove(DirectCast(ListBox1.SelectedItem, cAddress))
ListBox1.Items.Remove(ListBox1.SelectedItem)
End If
End Sub
I changed the name of the list box to ListBox1 to match my test project.
Here is something to try, use a BindingSource for setting up the ListBox. In the class override ToString to what is to be shown in the ListBox rather than what you are doing now without a DataSource.
My version of your class has name and property name changes.
Public Class Address
Public Property Name() As String
Public Property Email() As String
Public Property Phone() As String
Public Property Comment() As String
Public Overrides Function ToString() As String
Return $"{Name} {Email} {Phone} {Comment}"
End Function
End Class
Mocked data is used to populate the ListBox
Public Class Form1
Private ReadOnly _bsAddresses As New BindingSource
Private Sub UpdateListBox()
Dim AddressList = New List(Of Address) From
{
New Address() With {
.Name = "John",
.Email = "john#gmail.com",
.Phone = "555-444-3456",
.Comment = "AAA"},
New Address() With {
.Name = "Mary",
.Email = "mary#gmail.com",
.Phone = "888-333-2222",
.Comment = "BBB"},
New Address() With {
.Name = "Bob",
.Email = "bob#gmail.com",
.Phone = "111-555-9999",
.Comment = "CCC"}
}
_bsAddresses.DataSource = AddressList
lstAddress.DataSource = _bsAddresses
lstAddress.SelectedIndex = 0
End Sub
Private Sub Form1_Shown(sender As Object, e As EventArgs) _
Handles Me.Shown
UpdateListBox()
End Sub
Private Sub RemoveButton_Click(sender As Object, e As EventArgs) _
Handles RemoveButton.Click
If lstAddress.Items.Count > 0 AndAlso lstAddress.SelectedItem IsNot Nothing Then
Dim address = CType(_bsAddresses.Current, Address)
If My.Dialogs.Question($"Remove {address.Name}") Then
_bsAddresses.RemoveCurrent()
RemoveButton.Enabled = _bsAddresses.Count > 0
End If
End If
End Sub
End Class
Code module for asking a question
Namespace My
<ComponentModel.EditorBrowsable(ComponentModel.EditorBrowsableState.Never)>
Partial Friend Class _Dialogs
Public Function Question(text As String) As Boolean
Return (MessageBox.Show(
text,
My.Application.Info.Title,
MessageBoxButtons.YesNo,
MessageBoxIcon.Question,
MessageBoxDefaultButton.Button2) = MsgBoxResult.Yes)
End Function
End Class
<HideModuleName()>
Friend Module WinFormsDialogs
Private instance As New ThreadSafeObjectProvider(Of _Dialogs)
ReadOnly Property Dialogs() As _Dialogs
Get
Return instance.GetInstance()
End Get
End Property
End Module
End Namespace
Karen's post seems quite comprehensive. My response is an attempt to focus on your direct question.
I don't see all of the type definitions shown in your code, but in answer to your question, which I believe is "Why am I getting System.ArgumentException: 'Argument 'Key' is not a valid value":
In the offending line of code:
AddressList.Remove(selectedName)
AddressList is a collection. To use .Remove, you must pass in an object of the AddressList collection to remove it. You are passing a simple string, and that is not an AddressList object. You need to create an object based on your string selectedName to pass into .Remove and that line should work. The rest of what you are doing seems more complex.

Saving ListBox with Additional information in My.settings

I recently started learning listboxes on VB.net and wanted to create something like a student list with additional information:
Private Class Students
Private m_Name As String
Private m_Age As String
Public Sub New(ByVal new_Name As String, ByVal new_Age As String)
m_Name = new_Name
m_Age = new_Age
End Sub
Public Overrides Function ToString() As String
Return m_Name
End Function
Public Function Age() As String
Return m_Age
End Function
End Class
So, students add to the listbox as follows:
ListBox1.Items.Add(New Students(StudentName.Text, StudentAge.Text))
'StudentName and StudentAge are textboxes.
but I also wanted to save this information so that it is automatically entered when the program is restarted. I tried to do first an event that saves every item in the ListBox but using the function above it doesn't work.
Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing
For Each item In ListBox1.Items
My.Settings.Students.Add(item)
My.Settings.Save()
Next
End Sub
Then I would like to load this information, but just like the event with saving information when closing the program it does not work.
I got an System.InvalidCastException The conversion of 'Students' to 'String' is not valid. error.
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
If My.Settings.Students Is Nothing Then
My.Settings.Students = New Specialized.StringCollection
Else
For Each item In My.Settings.Students
ListBox1.Items.Add(item)
Next
End If
End Sub
I would like to have these the information in listbox after load.
My.Settings is a feature that enables you to manage your application's settings such as; the last location and size of a form, the last selected properties of controls, recently opened files, ...etc. It's not - as already commented above - a place to store and retrieve your objects' data. Your data should be in a database or serialized as an alternative. Let's try the serialization approach to solve your problem.
Create your Serializable objects
The Student class:
Imports System.IO
Imports System.Xml.Serialization
Imports System.Runtime.Serialization
Imports System.Runtime.Serialization.Formatters.Binary
<Serializable()>
Public Class Student
#Region "Enums"
<Serializable()>
Public Enum Genders
Unknown
Female
Male
End Enum
#End Region
#Region "Constructors"
Public Sub New() : End Sub
Public Sub New(firstName As String, lastName As String, dob As DateTime, gender As Genders)
Me.FirstName = firstName
Me.LastName = lastName
Me.DOB = dob
Me.Gender = gender
End Sub
#End Region
#Region "Public Properties"
Public Property FirstName As String
Public Property LastName As String
Public ReadOnly Property FullName As String
Get
Return $"{FirstName} {LastName}"
End Get
End Property
Public Property DOB As DateTime
Public ReadOnly Property Age As Integer
Get
If DOB = Nothing Then Return 0
Dim y As Integer = Today.Year - DOB.Year
If DOB > Today.AddYears(-y) Then y -= 1
Return y
End Get
End Property
Public Property Gender As Genders
#End Region
#Region "Public Methods"
Public Overrides Function ToString() As String
Return $"{FullName}, {Age}, {Gender}"
End Function
#End Region
End Class
The Students class:
<Serializable()>
Public Class Students
#Region "Public Properties"
Public Property List As New List(Of Student)
#End Region
#Region "Public Functions"
Public Sub SaveXml(filePath As String)
Dim xs As New XmlSerializer(GetType(Students))
Try
Using fs As New FileStream(filePath, FileMode.Create)
xs.Serialize(fs, Me)
End Using
Catch ex As SerializationException
Console.WriteLine("Failed to serialize!")
End Try
End Sub
Public Shared Function LoadXml(filePath As String) As Students
Dim students As Students = Nothing
Dim xs As New XmlSerializer(GetType(Students))
Try
Using fs As New FileStream(filePath, FileMode.Open)
students = DirectCast(xs.Deserialize(fs), Students)
End Using
Catch ex As SerializationException
Console.WriteLine("Failed to deserialize!")
End Try
Return students
End Function
'Or if you prefer the binary format.
Public Sub SaveBytes(filePath As String)
Dim bf As New BinaryFormatter
Try
Using fs As New FileStream(filePath, FileMode.Create)
bf.Serialize(fs, Me)
End Using
Catch ex As SerializationException
Console.WriteLine("Failed to serialize!")
End Try
End Sub
Public Shared Function LoadBytes(filePath As String) As Students
Dim students As Students = Nothing
Dim bf As New BinaryFormatter
Try
Using fs As New FileStream(filePath, FileMode.Open)
students = DirectCast(bf.Deserialize(fs), Students)
End Using
Catch ex As SerializationException
Console.WriteLine("Failed to deserialize!")
End Try
Return students
End Function
#End Region
End Class
Your new classes in action!
In your form which contains the ListBox, create a class level variable of BindingSource type which will be used to populate the list box, add, edit, and remove Student objects.
Private bs As BindingSource
In your Form's constructor or Load event, load or create a new object of Students type, create a new instance of the BindingSource and attach it to the ListBox's DataSource property as follow:
If String.IsNullOrEmpty(My.Settings.MyStudentsFile) Then
'XML file.
My.Settings.MyStudentsFile = Path.Combine(My.Application.Info.DirectoryPath, "MyStudentsData.xml")
'or a binary file name if you prefer the binary formatter, for example.
'My.Settings.MyStudentsFile = Path.Combine(My.Application.Info.DirectoryPath, "MyStudentsData.dat")
End If
If File.Exists(My.Settings.MyStudentsFile) Then
obj = Students.LoadXml(My.Settings.MyStudentsFile)
'or
'obj = Students.LoadBytes(My.Settings.MyStudentsFile)
End If
If obj Is Nothing Then obj = New Students
bs = New BindingSource(obj, "List")
ListBox1.DataSource = bs
Where the My.Settings.MyStudentsFile is the path of the data file.
What will be displayed in the list box is what the ToString returns for each Student object. If you'd like instead to display the FullName only, then set the DisplayMember property of the list box as follow:
ListBox1.DisplayMember = "FullName"
To add new Student:
Dim stu As New Student(
"FirstName",
"LastName",
New DateTime(2000, 5, 22),
Student.Genders.Male
)
bs.Add(stu)
ListBox1.ClearSelected()
ListBox1.SelectedItem = stu
To modify the properties of a Student entry:
If bs.Current IsNot Nothing Then
Dim stu As Student = DirectCast(bs.Current, Student)
stu.FirstName = "CorrectFirstName"
stu.LastName = "CorrectLastName"
stu.Gender = Student.Genders.Female
bs.ResetCurrentItem()
End If
To remove entries:
If bs.Current IsNot Nothing Then
bs.RemoveCurrent()
End If
'In case of multixxx SelectionMode
ListBox1.SelectedItems.OfType(Of Student).ToList.ForEach(Sub(stu) bs.Remove(stu))
To display a selected item in other controls, handle the SelectedValueChanged event of the list box as follow:
Private Sub ListBox1_SelectedValueChanged(sender As Object, e As EventArgs) Handles ListBox1.SelectedValueChanged
If bs.Current IsNot Nothing Then
Dim stu As Student = DirectCast(bs.Current, Student)
TextBox1.Text = stu.FirstName
TextBox2.Text = stu.LastName
DateTimePicker1.Value = If(stu.DOB <> Nothing, stu.DOB, Now)
TextBox3.Text = stu.Age.ToString
End If
End Sub
Finally, to save your data in Form.FormClosing event:
bs.EndEdit()
Dim stus As Students = DirectCast(bs.DataSource, Students)
stus.SaveXml(My.Settings.MyStudentsFile)
'or
'stus.SaveBytes(My.Settings.MyStudentsFile)
bs.Dispose()
That's it all.

Setting database class at run time in VB.Net

I have the follow classes for Sqlite and SqlServer:
Class for SQLite:
Imports System.Data.SQLite
Public Class clsOperDB_SQLite
Public Shared Function getValue(sql As String) As String
Try
Using conn As New SQLiteConnection(strConn_SQLITE)
Using cmd As New SQLiteCommand()
cmd.Connection = conn
conn.Open()
cmd.CommandText = sql
Return cmd.ExecuteScalar
End Using
End Using
Catch ex As Exception
MsgBox(ex.Message)
End Try
Return ""
End Function
End Class
Class for SQLSERVER:
Imports System.Data.SqlClient
Public Class clsOperDB_SQLSERVER
Public Shared Function getValue(sql As String) As String
Try
Using conn As New SqlConnection(strConn_SQLSERVER)
Using cmd As New SqlCommand()
cmd.Connection = conn
conn.Open()
cmd.CommandText = sql
Return cmd.ExecuteScalar
End Using
End Using
Catch ex As Exception
MsgBox(ex.Message)
End Try
Return ""
End Function
End Class
this is my test form:
Public Class Form1
'form level variable
Dim dbConnector
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim connectionType As String = "SQLITE"
' need something like this or any way to set form level variable
If connectionType = "SQLITE" Then
dbConnector = clsOperDB_SQLite
Else
dbConnector = clsOperDB_SQLSERVER
End If
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
'get value from SQLITE
Dim ValueFromDatabase As String = dbConnector.getValue("select .....")
End Sub
End Class
I need help to define dbConnector variable and set its value, also intellisense should show me class methods, using a parameter I want to change database and avoid using a conditional for every time I want to use one or the other database :
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Dim Query As String = "Select ..."
Dim ValueFromDatabase As String = ""
' dont want to use if for each sql query
If connectionType = "SQLITE" Then
ValueFromDatabase = clsOperDB_SQLite.getValue(Query)
Else
ValueFromDatabase = clsOperDB_SQLSERVER.getValue(Query)
End If
End Sub
The rest of methods and params for both classes are the same only change class data objects (SQLiteConnection, SqlConnection, and so)
Thanks
You should define an interface that species all the common members. You can then create a class for each data source that implements that interface. In your application code, you can then declare a variable of that interface type and assign an instance of any class that implements it to that variable. You can then just use that variable and invoke any member of the interface without caring what type the actual class instance is.
The interface and the implementing classes would look something like this:
Public Interface IDataAccessProvider
Function GetValue(sql As String) As String
End Interface
Public Class SqliteDataAccessProvider
Implements IDataAccessProvider
Public Function GetValue(sql As String) As String Implements IDataAccessProvider.GetValue
'Provide SQLite-specific implementation here.
End Function
End Class
Public Class SqlServerDataAccessProvider
Implements IDataAccessProvider
Public Function GetValue(sql As String) As String Implements IDataAccessProvider.GetValue
'Provide SQL Server-specific implementation here.
End Function
End Class
Your application code might then look like this:
Private dataAccessProvider As IDataAccessProvider
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'Read a value that identifies the data source and store it here.
'The value might be stored in the config file or wherever else is appropriate.
Dim dataSourceIdentifier As String
Select Case dataSourceIdentifier
Case "SQLite"
dataAccessProvider = New SqliteDataAccessProvider()
Case "SQL Server"
dataAccessProvider = New SqlServerDataAccessProvider()
End Select
End Sub
You can then just call dataAccessProvider.GetValue in your code without any care for what the data source actually is, except to ensure that your SQL syntax is valid for that data source.
Please note that, while what you do is up to you, I have chosen to use a sane naming convention in this code. No one would last long working for me using class names like clsOperDB_SQLSERVER. There's a reason you don't see any types with names like that in the .NET Framework.

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