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!