Skip navigation

Category Archives: VB.Net

If you’re like me, then you like to experiment with new and different ways to do the usual programming tasks that you do. Perhaps its just a reaction to the boredom of always doing things the same way. Sometimes its an attempt to make the easy but time consuming tasks faster. Often is in a search for better looking, more abstract code. I like to think of it as a learning opportunity. The other day I was playing with sorting a list of objects. There are build in ways to do this in the List(Of T) class, so I though I would experiment with the different overloads of the List(Of T).Sort() method. While what is provided by the .NET Framework is adequate to make clean code, I played around anyways because you never know when you will run into something that doesn’t give you a clean solution out of the box. So just in case, its good to add more tools to your mental utility belt, at least that’s what I tell myself so it doesn’t feel like I’m wasting time.

For sorting an object based on an arbitrary property, the Sort() is not helpful as you cannot specify how the comparison is to be made. So that takes us to the first overload, Sort(IComparer(Of T)). This method is probably too cumbersome for standard adhoc and one time sorts that you may have to do. It requires that you pass it an instance of a class that implements IComparer(Of T), and the Compare method of that class will define your comparisons. One of my favorite features in .NET is generic types, as it increases code reuse substantially. So, why not make a generic class that takes a lambda function in the constructor to use as the comparison?


Public Class GenericCompare(Of T)
    Implements System.Collections.Generic.IComparer(Of T)

    Dim Func As Func(Of T, T, Integer)

    Public Sub New(ByVal Func As Func(Of T, T, Integer))
        Me.Func = Func
    End Sub

    Public Function Compare(ByVal x As T, ByVal y As T) As Integer Implements System.Collections.Generic.IComparer(Of T).Compare
        Return Func(x, y)
    End Function

End Class

Assuming a general object to sort like this one:


Public Class SomeObject

    Public Property Name As String

    Public Property Value1 As String

    Public Property Value2 As String

End Class

That will make our sort look like this:


        'Sort with custom compare
        Items.Sort(New GenericCompare(Of SomeObject)(Function(x, y)
                                                         If x.Name > y.Name Then
                                                             Return 1
                                                         ElseIf x.Name < y.Name Then
                                                             Return -1
                                                         Else
                                                             Return 0
                                                         End If
                                                     End Function))

The above uses a long implementation for the comparison, which could be used for customizing the sort order or sorting on multiple fields if needed. If you just want a basic string comparison of one property, you can shorten the above to this:


        'Or, sort with default string compare, one liner
        Items.Sort(New GenericCompare(Of SomeObject)(Function(x, y) String.Compare(x.Name, y.Name)))

Going crazy with this concept, you could even introduce an extension method to add another overload to Sort().


Public Module ExtensionMethods

    <System.Runtime.CompilerServices.Extension()>
    Public Sub Sort(Of T)(ByVal List As List(Of T), ByVal Func As Func(Of T, T, Integer))
        List.Sort(New GenericCompare(Of T)(Func))
    End Sub

End Module

Then your sort becomes this:


        'Or with the extension method 
        Items.Sort(Function(x As SomeObject, y As SomeObject) String.Compare(x.Name, y.Name))

I’m not sure why exactly, but if you notice in this usage, I had to specify the type for the x, y arguments. Unlike the last example, the compiler isn’t able to infer the type of the arguments.

Now, if you are looking at the different overloads for the Sort method, you may have notice that there is another overload that does basically what we just created. List(Of T).Sort(Comparison(Of T) takes a delegate to a function. So in this usage you would just have to create a function in your code that follows the Comparison(Of T) delegate. As we know however, a lambda expression can be used anywhere a delegate is expected, so we can shorten this up into a one-liner as well.


        Items.Sort(New Comparison(Of SomeObject)(Function(x As SomeObject, y As SomeObject) String.Compare(x.Name, y.Name)))

Although, the New Comparison(Of SomeObject)(...) is actually not necessary, and the use of this override can be shortened to the equivalent of the extension method example above.


Items.Sort(Function(x As SomeObject, y As SomeObject) String.Compare(x.Name, y.Name))

OK, so I played around a lot with the List(Of T).Sort methods and concluded that sorting a list of objects on an arbitrary property is pretty easy, and that .NET provides an overload that accepts a delegate or lambda for the comparison. So did I waste my time inventing that first method? Possibly, but I did prove to myself that you can create a generic class that can be made to accept lambdas that define its behavior. I can safely say I learned something and added another technique to the old toolbox, so in my onion a great use of time.

One skill they say is needed for software development is problem solving, and as a developer myself I know this to be true. It often seems like not a day will go by without encountering something outside of the original plan. Just the other day I was testing an application, which simply reads some data from a MS Access database. The database is part of a purchased software product, so I don’t really have any choice in the matter, it must be Access. Then up pops an exception The 'Microsoft.Jet.OLEDB.4.0' provider is not registered on the local machine.

Well some internet research quickly found that the default Jet database driver is not 64 bit compatible. The official resolution? Force your application to be 32 bit and all will be well. Well, not today. Although I hate leaving such decisions up dumb incompatibility issues, in this case I couldn’t make my app 32 bit. The application is an add-on extension to AutoCAD, which, if your running 64 bit windows, will only install as 64 bit. That means that any assemblies it loads, like mine, must also run 64 bit.

So, here I though I was up that darn creek again without my paddle. But low and behold I found my original research was a little dated, there was a new 64 bit driver that could be used for Access databases. It turns out, that even though Microsoft really wanted to steer developers away from using Access in favor of SQL Express, they had to make a driver so that the MS Office suite could be 64 bit. Ok, cool. So I download the new driver, but of course, it doesn’t install. It complains that I must first uninstall my 32 bit version of Office before installing the driver. Ok, I definitely don’t want to go down this road, because not only does it mean making sure that this new driver gets installed on the users PC, but that the correct version of Office is on there as well.

Well, there I sat, convinced that this would either not work or be one of those things I regret doing much later. Then a little inspiration. I though, maybe I just need a separate assembly that can run 32 bit? Well that’s not possible, the 32/64 bit decision is made for the whole process, not just an assembly. It would have to be a separate executable.
I started to feel good about this, a separate ‘proxy’ executable to interact with access for me. The interprocess communication wouldn’t be bad, I would just have to send it SQL statements and it could send me back the results as a DataTable. The DataTable object has its own ReadXML and WriteXML functions, so the matter of moving the object would be simple.

Ok, so a separate executable. That would mean another project in my solution and in source control. It would also have to get deployed with the client with a known location so it can be started when its needed. Not really that big of a hurdle, but I wanted to try for something else anyways. More internet research turned up some example of using the CodeDOMProvider class to compile and emit an executable file. I liked that concept, create the executable on demand from a short piece of code. Wrap all this up in a single class and I’m happy.

First thing to do was write the proxy code. I really only needed two operations from the database. The ‘Execute Scalar’ option to return a singe value, and an option to fill a DataTable object with results from an query. If I used the System.Diagnostics.Process to start the proxy process, I could read and write to the processes standard input and standard output. That way the proxy could use the Console.In and Console.Out streams to handle its communication with the client process.

So I pass the proxy the connection string on the command line, so it can create a connection object to be used later. Then it reads from standard input. I will send it commands and end the commands with the null character. Once it reads the command, either ‘GetDT’, ‘Scalar’, or ‘Die’, it then looks for the query to written to standard input, again terminated with the null character. It executes the query, then either writes the singe value to standard out for scalar, or it writes the XML representation of the DataTable object. The calling process reads the result and we are done.

Now the client class was designed to be created, used, and then closed. The constructor, which also contains the source code to the proxy as a simple string constant, compiles the proxy code and generates the executable file. The constructor then runs the executable, saving the Process object so the standard in and out can be used later. The object then defines a function for each command it can run in the proxy. The function sends the commands and retrieves the result from the proxy’s standard output. When you are done using the object, simply call the Close method to send the proxy the ‘Die’ command. When the proxy receives this command the process exists.

See the full class code below:

Imports System.CodeDom.Compiler


Public Class Access64Bridge

    Private Proc As Process


    Public Sub New(ByVal ConnectionString)

        'just use the temp folder for the exe location
        Dim output As String = My.Computer.FileSystem.SpecialDirectories.Temp & "Access64Bridge.exe"

        Dim CDP As CodeDomProvider = CodeDomProvider.CreateProvider("VB")
        Dim params As New CompilerParameters
        params.OutputAssembly = output
        params.GenerateExecutable = True
        'Compiler options, most important are x86 flag and winexe
        '/target:winexe doesn't show a console window
        params.CompilerOptions &= "/platform:x86 /optioninfer /optionstrict- /target:winexe"
        'Add references required by code
        params.ReferencedAssemblies.Add("System.Data.dll")
        params.ReferencedAssemblies.Add("System.Xml.dll")
        params.ReferencedAssemblies.Add("Microsoft.VisualBasic.dll")

        'CDATA xml trick from the internet, good way to do multiline 
        'string literal like some other languages allow
        Dim Source As String = <![CDATA[
        Imports System
        Imports System.Data
        Imports Microsoft.VisualBasic

        Class Test
        Public Shared Sub Main(ByVal args() As String)
            Try
                Dim connstr As String = args(0)
            Dim conn = New
                OleDb.OleDbConnection(connstr)

                Do
                    'Get Command
                    Dim Comm As String = ReadInputValue()
                    Select Case Comm
                        Case "GetDT"
                            'Next thing in is the SQL
                            Dim SQL As String = ReadInputValue()

                            Dim DT As DataTable = New DataTable()
                            Dim DAObjOLE As OleDb.OleDbDataAdapter = New OleDb.OleDbDataAdapter
                            Dim CommandObj As New OleDb.OleDbCommand(SQL, conn)

                            conn.Open()
                            DAObjOLE.SelectCommand = CommandObj
                            DAObjOLE.Fill(DT)
                            conn.Close()

                            DT.TableName = "Output"
                            'Serialize data table to the
                            output()
                            DT.WriteXml(Console.Out,
                            XmlWriteMode.WriteSchema)
                            'End with the null char
                            Console.Out.Write(vbNullChar)
                        Case "Scalar"
                            'Next thing in is the SQL
                            Dim SQL As String = ReadInputValue()
                            Dim CommandObj As New OleDb.OleDbCommand(SQL, conn)

                            conn.Open()
                            Dim Value As Object = CommandObj.ExecuteScalar
                            conn.Close()


                            Console.Out.Write(Value.ToString)
                            Console.Out.Write(vbNullChar)
                        Case "Die"
                            Return
                        Case Else
                            'Unrecognized command, host possibly is gone, just end
                            Return

                    End Select

                Loop
            Catch ex As Exception
                MsgBox("Access 64 to 32 bit Bridge: " &
                ex.Message & ex.StackTrace)
            End Try
        End Sub

        Public Shared Function ReadInputValue() As String
            Dim Value As New Text.StringBuilder
            Dim ch As Integer = Console.In.Read
            While ch <> 0 And ch <> -1
                Value.Append(Chr(ch))
                ch = Console.In.Read
            End While
            Return Value.ToString
        End Function
    End Class

        ]]>.Value

        Dim Result As CompilerResults = CDP.CompileAssemblyFromSource(params, Source)
        Dim X As New System.Diagnostics.ProcessStartInfo
        'Must redirecte standard in and out so we can use it
        X.RedirectStandardOutput = True
        X.RedirectStandardInput = True
        'Path of executable to run
        X.FileName = output
        'Required for redirection
        X.UseShellExecute = False
        'Connection string surrounded by double quotes so it comes
        'into the proxy as a single argument
        X.Arguments = """" & ConnectionString & """"
        'Start Process
        Proc = Process.Start(X)


    End Sub

    ''' <summary>
    ''' Reads a value from the access bridge program
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Function ReadValue() As String
        Dim Value As New Text.StringBuilder
        Dim ch As Integer = Proc.StandardOutput.Read
        'use null termniated messages, -1 means the stream is dead
        While ch <> 0 And ch <> -1
            Value.Append(Chr(ch))
            ch = Proc.StandardOutput.Read
        End While
        Return Value.ToString
    End Function

    ''' <summary>
    ''' Runs sql and returns the first result
    ''' </summary>
    ''' <param name="Sql"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function ExecuteScalar(ByVal Sql As String) As Object
        'Command
        Proc.StandardInput.Write("Scalar")
        'Null terminate
        Proc.StandardInput.Write(vbNullChar)
        'Sql
        Proc.StandardInput.Write(Sql)
        'Null termniate
        Proc.StandardInput.Write(vbNullChar)

        Return ReadValue()
    End Function

    ''' <summary>
    ''' Executes the SQL on the bridge and returns the resulting DataTable
    ''' </summary>
    ''' <param name="Sql"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function CreateDT(ByVal Sql As String) As DataTable
        'Command
        Proc.StandardInput.Write("GetDT")
        'Null terminate
        Proc.StandardInput.Write(vbNullChar)
        'Sql
        Proc.StandardInput.Write(Sql)
        'Null termniate
        Proc.StandardInput.Write(vbNullChar)

        Dim Value = ReadValue()
        Dim DT As New DataTable
        DT.ReadXml(New IO.StringReader(Value))
        'Accept changes else all rows will be new
        'New rows are removed from the collection when deleted
        DT.AcceptChanges()
        Return DT
    End Function

    Public Sub Close()
        'Command
        Proc.StandardInput.Write("Die")
        'Null terminate
        Proc.StandardInput.Write(vbNullChar)
    End Sub
End Class


You never know what your going to have to do to get the job done.

In my last article, I talked a little bit about taking time to program for yourself. The example used was a drop-down list control that automatically filled itself from an enumeration. Today, we will be looking at the process of filling a form from, and copying data back to, a class. A typical situation for software that deals with user input. Whether its data collection, sales order entry, or whatever, the standard CRUD (create,read,update,delete) theme is repeated over and over again. With anything that is repeated over and over, it can, and probably should be automated. The following is a simplified example of how to replace the process of manually ‘gluing’ your forms controls to the underlying class object.

I’ve explored the process of doing this before in other projects. The original scheme was to create a base class that all other controls/forms would inherit from that contained all the generic code for filling the controls and updating the class. Although this worked quite well, you can always learn something from exploring a new approach. So, instead of using a universal base control/form, we will instead be using a Custom Provider Control. To explain, this is similar to how the ToolTip component works. It provides a service to the form/control its placed on, and adds a property to each control in the property designer. So our control will provide the service of filling the form with class data, and updating the class data when the user changes what’s on the form.

First things first, we need to create or provider control. If you read the linked article above, you see that we just have to create a new class that inherits from System.ComponentModel.Component, and implements the System.ComponentModel.IExtenderProvider interface. It also needs to have a System.ComponentModel.ProvideProperty attribute, which give the property to be added to controls in the property wizard and also give a control type filter.

<System.ComponentModel.ProvideProperty("PropertyName", GetType(Control))>
Public Class ControlMapper
    Inherits System.ComponentModel.Component
    Implements System.ComponentModel.IExtenderProvider

The property name we give to the ProvideProperty attribute is “PropertyName”, since what we are mapping our controls to are public properties of a class. The type filter we give it is just Control, since we could bind a property to almost any type of control we can code for. That leads us to the implementation of the IExtenderProvider interface, which contains only one method, CanExtend. CanExtend is called for any control in the form and we return True if our class will extend it, or False if it will not.

    Public Function CanExtend(ByVal extendee As Object) As Boolean Implements System.ComponentModel.IExtenderProvider.CanExtend
        If extendee.GetType Is GetType(TextBox) Then
            Return True
        Else
            Return False
        End If
    End Function

For simplicity of the example, we will only deal with text boxes. I’ll leave it as an exercise for you to extend the class to handle other controls like combo boxes, picture boxes, calendars, etc. Now, the next thing that needs to be created is a Get and Set function for the property name we gave to the ProvideProperty attribute, in the format of GetPropertyName and SetPropertyName. These functions will use a private member Mapped which is an instance of Dictionary(of Control, String). This local will track the controls that are being associated to the provider and the property name that was set in the designer. We also set up and event handler when the property is set, so that we can update the forms class when the text is changed by the user.


    Public Function GetPropertyName(ByVal myControl As Control) As String
        'Get property name based on control, return empty string if not found
        If Mapped.ContainsKey(myControl) Then
            Return Mapped.Item(myControl)
        Else
            Return ""
        End If
    End Function

    Public Sub SetPropertyName(ByVal myControl As Control, ByVal value As String)
        'Add property/control pair to dictionary
        If Mapped.ContainsKey(myControl) Then
            Mapped.Item(myControl) = value
        Else
            Mapped.Add(myControl, value)
        End If
        'Add event handler for control type
        If myControl.GetType() Is GetType(TextBox) Then
            Dim tb As TextBox = myControl
            AddHandler tb.TextChanged, AddressOf Me.TextChanged
        End If
    End Sub

Now, we need to add a public property to the provider itself, so the form using it can give it a pointer to the class object that the form is editing.

Public Property FormObject As Object

And we will add a private field, to mark when we are filling the form. This will server to let us ignore events while we populate controls with class object data.

Dim Filling As Boolean = False

Now, the first thing we have to do before we edit data, is that we must load it to be edited. So a method is needed to fill all the associated controls with the value of the property they were mapped to. We first loop through the KeyValuePair(of Control, String) that are stored in our Mapped dictionary, this gives us the control object and the name of the property it is bound to.

Dim Cntl As Control = item.Key
Dim PropertyName As String = item.Value

Then, using reflection, we can retrieve the PropertyInfo for the property name and use it to get the value from the class object.

Dim Prop = FormObject.GetType().GetProperty(PropertyName)
Value = Prop.GetValue(FormObject, Nothing)

Here’s the complete fill method, complete with null checks, etc.

    Public Sub FillFromClass()
        'Check for null
        If FormObject IsNot Nothing Then
            Try
                'Set filing flag, use try...finally structure to 
                'ensure the flag is always turned off
                Filling = True
                'Loop through all handled controls
                For Each item In Mapped
                    'Get control on property name from dictionary item
                    Dim Cntl As Control = item.Key
                    Dim PropertyName As String = item.Value
                    'Get value from class 
                    Dim Value As Object
                    'Get reflection property
                    Dim Prop = FormObject.GetType().GetProperty(PropertyName)
                    'Check null 
                    If Prop IsNot Nothing Then
                        'get property value
                        Value = Prop.GetValue(FormObject, Nothing)
                        'Set value according to control type 
                        If Cntl.GetType() Is GetType(TextBox) Then
                            'Set text property for text boxes
                            Dim TB As TextBox = Cntl
                            TB.Text = Value
                            'Done!
                        End If
                    End If
                Next
            Finally
                Filling = False
            End Try
        End If
    End Sub

As you may have seen in the SetPropertyName method, we are adding an event handler to the TextChanged event of the text box. This event will handle updating the FormObject with the value from the modified control. We can use the GetPropertyName function to get the name of the property the control is bound to so that we can update FormObject using reflection.

    Private Sub TextChanged(ByVal sender As Control, ByVal e As EventArgs)
        'Check for DesingMode
        If Not DesignMode Then
            'Check if the control is being filled
            If Not Filling Then
                'Check for null
                If FormObject IsNot Nothing Then
                    'Get the name of the property bound to control
                    Dim PropetyName = GetPropertyName(sender)
                    'Check if found
                    If PropetyName <> "" Then
                        'Get property using reflection
                        Dim Prop = FormObject.GetType().GetProperty(PropetyName)
                        'Check null
                        If Prop IsNot Nothing Then
                            'Get value based on control type 
                            Dim Value As Object
                            If sender.GetType() Is GetType(TextBox) Then
                                'Get text property
                                Value = CType(sender, TextBox).Text
                            Else
                                'Unsupported control type
                                Return
                            End If
                            'Set the value of the class to the new value
                            Prop.SetValue(FormObject, Value, Nothing)
                            'All done!
                        End If
                    End If
                End If
            End If
        End If
    End Sub

That completes our provider. Here’s the complete code.


<System.ComponentModel.ProvideProperty("PropertyName", GetType(Control))>
Public Class ControlMapper
    Inherits System.ComponentModel.Component
    Implements System.ComponentModel.IExtenderProvider

    ''' <summary>
    ''' Dictionary to hold control/property mapping
    ''' </summary>
    ''' <remarks></remarks>
    Dim Mapped As Dictionary(Of Control, String)

    Public Sub New()
        'Instantiate the dictionary
        Mapped = New Dictionary(Of Control, String)
    End Sub

    ''' <summary>
    ''' Get function used by the ProvideProperty attribute
    ''' </summary>
    ''' <param name="myControl"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function GetPropertyName(ByVal myControl As Control) As String
        'Get property name based on control, return empty string if not found
        If Mapped.ContainsKey(myControl) Then
            Return Mapped.Item(myControl)
        Else
            Return ""
        End If
    End Function

    ''' <summary>
    ''' Set function used by the ProvideProperty attribute
    ''' </summary>
    ''' <param name="myControl"></param>
    ''' <param name="value"></param>
    ''' <remarks></remarks>
    Public Sub SetPropertyName(ByVal myControl As Control, ByVal value As String)
        'Add property/control pair to dictionary
        If Mapped.ContainsKey(myControl) Then
            Mapped.Item(myControl) = value
        Else
            Mapped.Add(myControl, value)
        End If
        'Add event handler for control type
        If myControl.GetType() Is GetType(TextBox) Then
            Dim tb As TextBox = myControl
            AddHandler tb.TextChanged, AddressOf Me.TextChanged
        End If
    End Sub

    ''' <summary>
    ''' The IExtenderProvider interface, returns true if the passed control type can be extended.
    ''' </summary>
    ''' <param name="extendee"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function CanExtend(ByVal extendee As Object) As Boolean Implements System.ComponentModel.IExtenderProvider.CanExtend
        If extendee.GetType Is GetType(TextBox) Then
            Return True
        Else
            Return False
        End If
    End Function

    ''' <summary>
    ''' Used to set the class the form will be updating
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property FormObject As Object

    ''' <summary>
    ''' Internal flag the the form is being filled
    ''' value changes should be ignored when true
    ''' </summary>
    ''' <remarks></remarks>
    Dim Filling As Boolean = False

    ''' <summary>
    ''' Called to initiallize all controls from the values stored in the class
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub FillFromClass()
        'Check for null
        If FormObject IsNot Nothing Then
            Try
                'Set filing flag, use try...finally structure to 
                'ensure the flag is always turned off
                Filling = True
                'Loop through all handled controls
                For Each item In Mapped
                    'Get control on property name from dictionary item
                    Dim Cntl As Control = item.Key
                    Dim PropertyName As String = item.Value
                    'Get value from class 
                    Dim Value As Object
                    'Get reflection property
                    Dim Prop = FormObject.GetType().GetProperty(PropertyName)
                    'Check null 
                    If Prop IsNot Nothing Then
                        'get property value
                        Value = Prop.GetValue(FormObject, Nothing)
                        'Set value according to control type 
                        If Cntl.GetType() Is GetType(TextBox) Then
                            'Set text property for text boxes
                            Dim TB As TextBox = Cntl
                            TB.Text = Value
                            'Done!
                        End If
                    End If
                Next
            Finally
                Filling = False
            End Try
        End If
    End Sub

    Private Sub TextChanged(ByVal sender As Control, ByVal e As EventArgs)
        'Check for DesingMode
        If Not DesignMode Then
            'Check if the control is being filled
            If Not Filling Then
                'Check for null
                If FormObject IsNot Nothing Then
                    'Get the name of the property bound to control
                    Dim PropetyName = GetPropertyName(sender)
                    'Check if found
                    If PropetyName <> "" Then
                        'Get property using reflection
                        Dim Prop = FormObject.GetType().GetProperty(PropetyName)
                        'Check null
                        If Prop IsNot Nothing Then
                            'Get value based on control type 
                            Dim Value As Object
                            If sender.GetType() Is GetType(TextBox) Then
                                'Get text property
                                Value = CType(sender, TextBox).Text
                            Else
                                'Unsupported control type
                                Return
                            End If
                            'Set the value of the class to the new value
                            Prop.SetValue(FormObject, Value, Nothing)
                            'All done!
                        End If
                    End If
                End If
            End If
        End If
    End Sub

End Class

Now, if you build you project, you should be able to drag and drop the provider control right onto your form.

Let’s create a simple class to test our new control.

Public Class ExampleClass

    Public Property Name As String

    Public Property City As String

    Public Property State As String

    Public Sub New()
        Name = "Testing Testerton"
        City = "Anytown"
        State = "WI"
    End Sub

End Class

Simple enough. We also need a form with some text boxes, like this.

After you drag a ControlMapper onto your form, you will notice that it appear in the designer in the component tray. You will also see that each text box will gain a new property.

Now for each of the text boxes, you just have to fill in the new PropertyName property with the name of the property in ExampleClass you want it to be mapped to. In the example form, we will create a new instance of ExampleClass and assign the ControlMapper1.FormObject that instance.

        FormObject = New ExampleClass
        ControlMapper1.FormObject = FormObject

Then to demonstrate that the form filling works, we will call ControlMapper1.FillFromClass() in the Load event of the form.

    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        'Fill the form with the class values
        ControlMapper1.FillFromClass()
    End Sub

Lastly, we can demonstrate that the updating works by displaying a message box with the updated values when we click on the button.

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim Msg As String = String.Format("Name: {0}, City: {1}, State: {2}", FormObject.Name, FormObject.City, FormObject.State)
        MsgBox(Msg)
    End Sub

Here’s the complete example form code.

Public Class Form1

    Dim FormObject As ExampleClass

    Public Sub New()
        ' This call is required by the designer.
        InitializeComponent()
        ' Add any initialization after the InitializeComponent() call.

        'Setup form object, and add to control mapper
        FormObject = New ExampleClass
        ControlMapper1.FormObject = FormObject
    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim Msg As String = String.Format("Name: {0}, City: {1}, State: {2}", FormObject.Name, FormObject.City, FormObject.State)
        MsgBox(Msg)
    End Sub

    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        'Fill the form with the class values
        ControlMapper1.FillFromClass()
    End Sub
End Class

Lets see this in action. First the form fill.

The let’s edit the values.

Let’s click the button to see if the class was updated.

It may seem a bit much for a filling a couple text boxes, but when multiple control types are supported, the forms get big, and there are lots of forms, such an automation of the fill-form/update-object cycle can be a big time saver when building your forms. It reduces coding needed to make the form and update the form, and by implementing a reusable component, reduces the risk of introducing errors when you update.

Download the Sample Project.