Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / WinForms

ComplexConverter - make configuration still more flexible

3.80/5 (3 votes)
17 Apr 2008CPOL2 min read 1   54  
Converting complex Object-structures to string and reverse. Storing the result in an application-setting improves configurations flexibility substantially
Download ComplexConverter_src - 18.74 KB

Introduction

What I'd like to share is a technique, how multiple data items can be collected and converted into one String. The String can be stored as an application-setting, and all the data can be restored from it by one call.
This is useful to persist arbitrary user-settings, Form-Bounds, Form-WindowState, Widths of Splitterpanels, Slider-Positions, Widths of Columnheaders and stuff like this.
Actually a complete Treeview can be made persistent, with each Treenodes .Text, .IsExpanded-Property or what else may be important to save.

Using the code

Storing or Restoring starts with the call

VB.NET
(Dim S As String)  
S = aComplexConverter.ConvertToString() 
or 
aComplexConverter.RestoreFromString(S)
Both calls will raise the same aComplexConverter.Convert() - event, where the user has to implement a pass-through through his data and "point out" each Item he wants to get stored/restored.

Here a sample for a complex storing:

It stores/restores two Form-Properties (WindowState, Bounds), a Treeview (each TreeNode.Text, TreeNode.IsExpanded), the current Column-Widths of a ListView and all its Items (each SubItems Text)
VB.NET
Private WithEvents _Memory As New ComplexConverter

Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs) _
        Handles MyBase.Load
    _Memory.RestoreFromString(My.Settings.Memory)
End Sub

Private Sub Form1_FormClosing( _
        ByVal sender As Object, ByVal e As FormClosingEventArgs) _
        Handles Me.FormClosing
    My.Settings.Memory = _Memory.ConvertToString()
End Sub

'''<summary>complex conversion for this form</summary>
Private Sub _Memory_Convert( _
        ByVal sender As Object, ByVal e As ComplexConverter.EventArg) _
        Handles _Memory.Convert
    'convert Form-Properties
    With Me
        e.ConvertValue(.WindowState)
        e.ConvertValue(.Bounds)
    End With
    'convert Treeview
    EnumerateNodes(TreeView1.Nodes, e)
    'convert ListViews Column-Withs
    For Each Col As ColumnHeader In Me.ListView1.Columns
        e.ConvertValue(Col.Width)
    Next
    'convert ListView-Items
    e.ConvertList(Of ListViewItem)(ListView1.Items)
    For Each LVI As ListViewItem In ListView1.Items
        'convert ListView-SubItems
        e.ConvertList(Of ListViewItem.ListViewSubItem)(LVI.SubItems)
        For Each SITM As ListViewItem.ListViewSubItem In LVI.SubItems
            e.ConvertValue(SITM.Text)
        Next
    Next
End Sub

Private Sub EnumerateNodes( _
        ByVal Nodes As TreeNodeCollection, ByVal e As ComplexConverter.EventArg)
    e.ConvertList(Of TreeNode)(Nodes)
    For Each Nd As TreeNode In Nodes
        e.ConvertValue(Nd.Text)
        EnumerateNodes(Nd.Nodes, e)
        'Workaround: Treenode.IsExpanded is readonly, so I can't restore it directly
        Dim B As Boolean = Nd.IsExpanded
        e.ConvertValue(B)
        If B Then Nd.Expand()
    Next
End Sub

Two types of data have to be distinguished: Values and Lists. Values are stored/restored as they are, their datatype is inferred by the generic ConvertValue(Of T)(ByRef Item As T)-method.

VB.NET
e.ConvertValue(Me.WindowState)

Lists are stored by only saving their .Count. They are restored by generating and adding Count unititialized items.
For that the "pointing out" of any type of List requires to pass the datatype of its Items by Type-Parameter to the
ConvertList(Of TItem As New)(ByVal List As IList)-method.

VB.NET
'convert ListView-Items
e.ConvertList(Of ListViewItem)(ListView1.Items)

Points of Interest

The hack in this is: the same code is used for storing and also for restoring. This has two positive effects:

  • The user defines the conversion for both directions in only one data-pass-through-code.
  • Only pure data needs to be stored. No additional information, about what type of data, in which order it occurs, or how to interpret (like e.g. Xml-Serialisation would do).
These additionals need not to be stored, but stay immanently in the handler-code of the Convert-event. So the stored string is very compact - e.g. a result of the above shown code-sample:
Normal|0; 0; 765; 321|2|Knoten0|2|Knoten1|0|False|Knoten2|3|Knoten4|0|False|Knoten5|0|False|Knoten6|0|
False|False|True|Knoten7|2|Knoten8|0|False|Knoten9|0|False|False|90|90|
90|90|2|4|01|02|03|04|4|11|12|13|14

Inside ComplexConverter

ComplexConverter has a problem, when set up, or conversion-code is changed. After a change it will read the old data, which doesn't fit no more.
For that RestoreFromString() raises the Convert-Event two times in two different modes: TryRestore-mode and Restore-mode
TryRestore imitates a restoring, but writes the values into a buffer. If an error occurs, the exception is catched and the restoring-process will be aborted without writing data to any target-property. So the targets will stay in their default-configuration.
Finally in the Restore-mode the data will be copied to the target-properties.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)