Making our Code More Dynamic

Making our Code More Dynamic

Rate This
  • Comments 12

In a previous post I showed how we could dynamically create a UI based on some XML, however the properties we wanted to set were known because we wrote the XML. I want to follow up with an extension of that sample that can dynamically set the properties on our objects that we're creating by reading the property names from the XML as well.

We need to rewrite the Dynamic class so that it can get and set the properties dynamically. We'll turn to VB's CallByName function to help us (this is similar to the SetProperty() VFP method). Currently this function only works with simple types, however, so we cannot dynamically set our colors with this technique, though it can still be powerful in many scenarios. Let's take a look:

My new XML document looks like the following: 

<?xml version="1.0" encoding="utf-8" ?>

<questions>

  <question>

    <assembly>System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</assembly>

    <control>System.Windows.Forms.TextBox</control>

    <text>This is the first survey question.</text>

    <height>35</height>

    <readonly>true</readonly>

    <tabstop>false</tabstop>

    <multiline>true</multiline>

  </question>

  <question>

    <assembly>System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</assembly>

    <control>System.Windows.Forms.TextBox</control>

    <text>This is the second survey question.</text>

    <height>100</height>

    <readonly>true</readonly>

    <tabstop>false</tabstop>

    <multiline>true</multiline>

  </question>

  <question>

    <assembly>System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</assembly>

    <control>System.Windows.Forms.Label</control>

    <text>This is the third survey question.</text>

    <height>80</height>

    <tabstop>false</tabstop>

  </question>

  <question>

    <assembly>System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</assembly>

    <control>System.Windows.Forms.Button</control>

    <text>This is the fourth survey question.</text>

    <height>30</height>

    <tabstop>false</tabstop>

  </question>

</questions>

 

Now we just read the list of properties from the document. We can do this easily in VB 8 using a DataSet again:

'Read the xml into a dataset for easier processing of the elements

Dim myData As New DataSet()

myData.ReadXmlSchema(CurDir() & "\questions.xsd")

myData.ReadXml(CurDir() & "\questions.xml")

Dim survey As DataTable = myData.Tables(0)

 

'Read the properties from the XML

Dim props As New List(Of String)

For Each dc As DataColumn In survey.Columns

    props.Add(dc.ColumnName)

Next

'Now add all the questions defined in the questions.xml file

For Each row As DataRow In survey.Rows

    Me.AddQuestion(row, props)

Next

Now we can pass this list of properties into the Dynamic function along with the info object which can either be a DataRow in this case or our static QuestionInfo object we created previously. Note that I didn't change the QuestionInfo class definition, this code will ignore any properties that are not found on the info object or ones that cannot be set on the return object. This makes the code more dynamic because now we don't necessarily know the type of the object we're creating or the properties that are being set on it:

Option Strict Off

 

Public Class Dynamic

 

    ''' <summary>

    ''' Dynamically creats an object and sets properties on it

    ''' by reading properties on the passed in object.

    ''' </summary>

    ''' <param name="info">An object with the property values</param>

    ''' <param name="properties">The list of properties to set from the info

    ''' object onto the created object</param>

    ''' <returns></returns>

    Shared Function GetQuestion(ByVal info As Object, ByVal properties As List(Of String)) As Object

        Dim c As Object

        Dim propValue As Object

        Try

 

            c = System.Reflection.Assembly.Load(info!Assembly).CreateInstance(info!Control)

 

           'VB does an automatic conversion at runtime

            For Each prop As String In properties

                Try

                   'Late bound call - we don't know the type or the value 

                    propValue = info(prop)

                Catch ex As Exception

                    propValue = Nothing

                End Try

 

                If propValue IsNot Nothing AndAlso propValue IsNot System.DBNull.Value Then

                    Try

                        CallByName(c, prop, CallType.Set, propValue)

                    Catch ex As Exception

                        'if we can't set a property on the object, just ignore

                    End Try

                End If

            Next

 

        Catch ex As Exception

            'Try/Catch is required here, as this code will cause

            ' a runtime error if the the type cannot be created.

            c = New TextBox

            c.Text = ex.ToString

            c.MultiLine = True

            c.Height = 100

            c.ReadOnly = True

            c.ScrollBars = ScrollBars.Vertical

        End Try

 

        Return c

    End Function

 

End Class

 

 I've attached the new sample to this post. No one can deny that VB still has a long way to go to enable better support here, but we can see that the beginings of dynamic programming in Visual Basic today.

Enjoy!

Attachment: DynamicUI.zip
Leave a Comment
  • Please add 6 and 7 and type the answer here:
  • Post
  • PingBack from http://blogs.msdn.com/bethmassi/archive/2007/07/17/an-example-of-dynamic-programming-in-vb.aspx

  • Making our Code More Dynamic See the beginings of dynamic programming in Visual Basic today.

  • Hey Beth,

    Now don't shoot me, BUT, again there's nothing that requires strict Off there. The only place it is used is i nthe final Catch block in which case it's jsut as easy to create a new textbox variable as Textbox then assing that to c (see my previous code in the previous blog)

    So... strangely enoguh, as this code becomes more dynamic in nature, you actually still don't need Strict Off semantics, the rela grunt work done here is CallByName.

    As to the differences between VB6 and VB.NET CallByName, the thing in VB6 is a lot of thigns ,such as OLEColor are intrinsic types underneath such as integer (double for date) etc.  I beleive in VB.NET you only get the intrinsic implicit conversions, e.g String to another intrinsic type.  

    I would have thought considering that CallByNAme examines the property type it could convert by perhaps lookign for TypeConvertors declared on the peoprty or the type itself.  Maybe that's something for the VB 10 list <g>

    Oh, BTW: I would have used CallType.Let considering these are simple values stored in strings ;)

  • So there is no pleeeeeeezing you. No offense, but I'm not suprised :-)

    The CallByName function does not need option strict off, but the late bound call to retrieve the value of the property is. The type and value of the property is not known. Unless you want to put a case statement in there that checks for every possible type and casts appropriately (hence, making the menthod inflexible) then you need strict off in this case.

    In this example, we don't know the type we're creating, the properties we're setting, the types and values of those properties nor the object that contains those values.

    I've already given Amanda a heads up on the CallByName function :-)

  • Damn straight there's no pleasing me ;)

    Shared Function GetQuestion(ByVal info As Object, ByVal properties As List(Of String)) As Object

    Dim c As Object

    Dim propValue As Object

    Try

    Dim assemblyName As String = CStr(CallByName(info, "Item", CallType.Get, "Assembly"))

    Dim controlName As String = CStr(CallByName(info, "Item", CallType.Get, "Control"))

    c = System.Reflection.Assembly.Load(assemblyName).CreateInstance(controlName)

    For Each prop As String In properties

    Try

    propValue = CallByName(info, "Item", CallType.Get, prop)

    Catch ex As Exception

    propValue = Nothing

    End Try

    If propValue IsNot Nothing AndAlso propValue IsNot System.DBNull.Value Then

    Try

    CallByName(c, prop, CallType.Set, propValue)

    Catch ex As Exception

    'if we can't set a property on the object, just ignore

    End Try

    End If

    Next

    Catch ex As Exception

    Dim tbx As New TextBox

    With tbx

    .Text = ex.ToString

    .Multiline = True

    .Height = 100

    .ReadOnly = True

    .ScrollBars = ScrollBars.Vertical

    End With

    c = tbx

    End Try

    Return c

    End Function

  • Your code is still dynamic :-)

  • Right !  And with Strict On !!   Such is the power of CallByName.  It'll be even nicer when we have dynamic identifiers in VB10

  • Beth and I have been having a argument all in fist fight discussion about dynamic code in VB. My point

  • Holy crap Bill! Did you just agree with my comment? NO WAY! :-)

  • Ha !  And there I was thinking you were implicitly agreeing with me ;)

  • LOL! When you getting your butt back to the states? You owe me a beer.... :-P

  • The beer's over here. You'll have to come to TehcEd down-under ;)

Page 1 of 1 (12 items)