ComplexConverter - make configuration still more flexible






3.80/5 (3 votes)
Converting complex Object-structures to string and reverse. Storing the result in an application-setting improves configurations flexibility substantially
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
(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)
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.
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 theConvertList(Of TItem As New)(ByVal List As IList)
-method.
'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).
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.