Erik Porter's Blog

Life and Development at Microsoft and Other Technology Discussions

News

    Serializing UI Thoughts and Code Experiments

    I was reading through some posts on various Microsoft Forums last night and I came across a few different people asking about being able to persist the contents of a TreeView to an Xml file (apparently other packages have this feature, like Java or something like that).  Can't say I've ever had a need for that (although I have persisted tree structured data to databases...that's pretty fun, actually), but it seemed interesting enough that I thought I'd give it a try.

    I started creating a custom TreeView Control, but as I went along, I was thinking, why should I come up with some custom hardcoded way to persist the Nodes Collection to Xml?  Why not just use Reflection to grab all the Properties and what not using recursion?  Sounds good, so I start coding.  However, I don't need to persist every single property.  How could I decide which ones need to be persisted and which ones don't?  Then I remember all the work I've been doing with the WindowsForms Designer stuff lately and was thinking, "man, why not use the TypeDescriptor?  It already knows which Properties should be persisted, etc".  So great, I throw that into the mix!  Then I start wondering though.  Why don't I make this a little more universal and actually persist entire controls?  So I get it working with persisting every property that needs to be persisted in the entire TreeView.  Here's the following code I came up with (it's pretty crude and needs cleaned up, so be nice  ;)):

    Private Sub RefreshXml()
        
    With _XmlDoc
             
    .RemoveAll()
             
    .AppendChild(.CreateElement("TreeView"))
             
    For Each PD As PropertyDescriptor In TypeDescriptor.GetProperties(Me)
                  
    If PD.SerializationVisibility <> DesignerSerializationVisibility.Hidden AndAlso PD.ShouldSerializeValue(Me) AndAlso PD.Name <> "XmlDoc" Then
                       
    SerializeChildObjects(_XmlDoc, .FirstChild, Me, PD)
                  
    End If
             
    Next
        
    End With
    End Sub

    Private Sub SerializeChildObjects(ByVal XmlDoc As XmlDocument, ByRef Node As XmlNode, ByVal Component As Object, ByVal PD As PropertyDescriptor)
        
    Dim ChildProperties As PropertyDescriptorCollection = PD.GetChildProperties

        
    If PD.PropertyType Is GetType(String) OrElse PD.PropertyType Is GetType(Color) OrElse ChildProperties.Count = 0 Then
             
    Dim Value As Object = PD.GetValue(Component)

             
    If PD.PropertyType Is GetType(String) AndAlso DirectCast(Value, String) = "" Then
                  
    Exit Sub
             
    End If

             
    If PD.PropertyType Is GetType(Color) AndAlso DirectCast(Value, Color).IsEmpty Then
                  
    Exit Sub
             
    End If

             
    Node.AppendChild(XmlDoc.CreateElement(PD.Name)).AppendChild(XmlDoc.CreateTextNode(Value.ToString))
        
    Else
             
    Dim NewNode As XmlNode = Node.AppendChild(XmlDoc.CreateElement(PD.Name))

             
    If Not Component Is Nothing Then
                  
    NewNode.Attributes.Append(XmlDoc.CreateAttribute("Type")).Value = PD.PropertyType.Name
             
    End If

             
    If IsCollection(PD.PropertyType) Then
                  
    If DirectCast(PD.GetValue(Component), ICollection).Count > 0 Then
                       
    Dim NewNewNode As XmlNode = NewNode.AppendChild(XmlDoc.CreateElement(PD.PropertyType.Name))

                        
    For Each Item As Object In DirectCast(PD.GetValue(Component), ICollection)
                            
    Dim NewNewNewNode As XmlNode = NewNewNode.AppendChild(XmlDoc.CreateElement(Item.GetType().Name))

                             For
    Each NewPD As PropertyDescriptor In
    TypeDescriptor.GetProperties(Item)
                                 
    If NewPD.SerializationVisibility <> DesignerSerializationVisibility.Hidden AndAlso (NewPD.ShouldSerializeValue(Item) OrElse IsCollection(NewPD.PropertyType)) Then
                                      
    SerializeChildObjects(XmlDoc, NewNewNewNode, Item, NewPD)
                                  
    End If
                             
    Next
                        
    Next
                   
    End If
              
    End If

             
    For Each NewPD As PropertyDescriptor In ChildProperties
                  
    If Not NewPD.IsReadOnly Then
                       
    SerializeChildObjects(XmlDoc, NewNode, PD.GetValue(Component), NewPD)
                  
    End If
             
    Next

             
    If NewNode.ChildNodes.Count = 0 Then
                  
    Node.RemoveChild(NewNode)
             
    End If
        
    End If
    End Sub

    Private Function IsCollection(ByVal Item As Type) As Boolean
        
    Return Not Item.GetInterface("IList") Is Nothing AndAlso Not Item.GetInterface("ICollection") Is Nothing
    End Function

    After that (unfortunately too late), it hit me.  WebServices already do this sort of thing for object serialization.  I'm sure most of this could be replaces by some already existing stuff.  In comes Matt Powell to the rescue!

    But then of course, my mind starts going crazy with possibilities.  Why not do this for entire forms?  For entire Projects (and Solutions).  Why not make a plug-in.  Then the practical me comes in and says, "but what's all this for?  how can you make it practical?"  Not much has come to mind yet on that.  However, a few thoughts and ideas have.

    Maybe this sort of thing could be used for some sort of super detailed documentation or code examples.  Just apply some XSL and get a nice page of code, etc.

    Then I started thinking about some of the things I've been hearing about LongHorn.  Some possibilities of declaring your WindowsForms UI with tags, more like ASP.NET and wondering if what I was messing around with is related.  Could I clean up my code into a "UISerializer" or something like that?

    Then, yet again, my brain kicks in and says "slow down...what would anybody use this for?"  At that point, I decided it was time for bed (6:00 AM  :P)

    So now I'm curious.  Is there anything of use in what I've been messing around with?  If you have any additional ideas or thoughts on this subject, I'd love to hear about them, or maybe even see a related blog entry.  If you happen to find some use out the posted code, let me know too!  :)

    /me needs a whiteboard badly!

    Comments

    TrackBack said:

    Don XML Blog (not that Don)
    # May 22, 2003 8:50 PM

    TrackBack said:

    HumanCompiler
    # May 22, 2003 8:50 PM