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
(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
Private Sub _Memory_Convert( _
ByVal sender As Object, ByVal e As ComplexConverter.EventArg) _
Handles _Memory.Convert
With Me
e.ConvertValue(.WindowState)
e.ConvertValue(.Bounds)
End With
EnumerateNodes(TreeView1.Nodes, e)
For Each Col As ColumnHeader In Me.ListView1.Columns
e.ConvertValue(Col.Width)
Next
e.ConvertList(Of ListViewItem)(ListView1.Items)
For Each LVI As ListViewItem In ListView1.Items
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)
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 the
ConvertList(Of TItem As New)(ByVal List As IList)
-method.
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.