Udostępnij za pośrednictwem


Making our Code More Dynamic

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!

DynamicUI.zip

Comments

  • Anonymous
    July 18, 2007
    PingBack from http://blogs.msdn.com/bethmassi/archive/2007/07/17/an-example-of-dynamic-programming-in-vb.aspx

  • Anonymous
    July 18, 2007
    Making our Code More Dynamic See the beginings of dynamic programming in Visual Basic today.

  • Anonymous
    July 18, 2007
    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 ;)

  • Anonymous
    July 19, 2007
    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 :-)

  • Anonymous
    July 19, 2007
    The comment has been removed

  • Anonymous
    July 19, 2007
    Your code is still dynamic :-)

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

  • Anonymous
    July 19, 2007
    The comment has been removed

  • Anonymous
    July 19, 2007
    The comment has been removed

  • Anonymous
    July 19, 2007
    Ha !  And there I was thinking you were implicitly agreeing with me ;)

  • Anonymous
    July 19, 2007
    The comment has been removed

  • Anonymous
    July 19, 2007
    The beer's over here. You'll have to come to TehcEd down-under ;)