Making our Code More Dynamic

Published 18 July 07 03:52 PM

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!

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

# Beth Massi - Sharing the goodness that is VB : An Example of Dynamic Programming in VB said on July 18, 2007 7:24 PM:

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

# Paul Mooney said on July 18, 2007 9:10 PM:

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

# Bill_McC said on July 18, 2007 11:27 PM:

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 ;)

# Beth Massi said on July 19, 2007 10:12 AM:

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 :-)

# Bill McCarthy said on July 19, 2007 10:37 AM:

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

# Beth Massi said on July 19, 2007 10:42 AM:

Your code is still dynamic :-)

# Bill McCarthy said on July 19, 2007 10:51 AM:

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

# @ Head said on July 19, 2007 11:03 AM:

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

# Beth Massi said on July 19, 2007 1:01 PM:

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

# Bill_McC said on July 19, 2007 10:07 PM:

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

# Beth Massi said on July 19, 2007 10:12 PM:

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

# Bill McCarthy said on July 20, 2007 3:44 AM:

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

Leave a Comment

(required) 
(optional)
(required) 

  
Enter Code Here: Required

About Beth Massi

Beth is a Program Manager on the Visual Studio Community Team at Microsoft and is responsible for producing and managing content for business application developers, driving community features and team participation onto MSDN Developer Centers (http://msdn.com), and helping make Visual Studio one of the best developer tools in the world. She also produces regular content on her blog (http://blogs.msdn.com/bethmassi), Channel 9, and a variety of other developer sites and magazines. As a community champion and a long-time member of the Microsoft developer community she also helps with the San Francisco East Bay .NET user group and is a frequent speaker at various software development events. Before Microsoft, she was a Senior Architect at a health care software product company and a Microsoft Solutions Architect MVP. Over the last decade she has worked on distributed applications and frameworks, web and Windows-based applications using Microsoft development tools in a variety of businesses. She loves teaching, hiking, mountain biking, and driving really fast.

This Blog

Syndication

Page view tracker