ComplexConverter - 使配置更加灵活






3.80/5 (3投票s)
将复杂的对象结构转换为字符串并反向转换。将结果存储在应用程序设置中可以显著提高配置的灵活性
引言
我想分享的是一种技术,可以收集多个数据项并将它们转换成一个字符串。该字符串可以存储为应用程序设置,并且所有数据都可以通过一次调用从该设置中恢复。
这对于持久化任意用户设置、窗体边界、窗体窗口状态、分隔面板的宽度、滑块位置、列标题的宽度等非常有用。
实际上,一个完整的 Treeview 树视图也可以被持久化,包括每个 TreeNode 节点的 .Text 属性、.IsExpanded 属性或其他需要保存的重要内容。
使用代码
存储或恢复从调用开始
(Dim S As String)
S = aComplexConverter.ConvertToString()
or
aComplexConverter.RestoreFromString(S)
这两个调用都将触发相同的 aComplexConverter.Convert()
事件,用户必须在其中实现对数据的传递,并“指出”他想要存储/恢复的每个项目。这里是一个复杂的存储示例
它存储/恢复两个窗体属性 (WindowState, Bounds
),一个 Treeview 树视图 (每个 TreeNode.Text, TreeNode.IsExpanded
),一个 ListView 的当前列宽度,以及它的所有项目 (每个 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
必须区分两种类型的数据:值和列表。值按原样存储/恢复,它们的数据类型由泛型方法 ConvertValue(Of T)(ByRef Item As T)
推断。
e.ConvertValue(Me.WindowState)
列表通过仅保存它们的 .Count
来存储。它们通过生成并添加 Count 个未初始化的项目来恢复。
为此,对于任何类型的列表的“指出”都需要通过 Type-Parameter 将其项目的 数据类型 传递给ConvertList(Of TItem As New)(ByVal List As IList)
方法。
'convert ListView-Items
e.ConvertList(Of ListViewItem)(ListView1.Items)
关注点
其中的技巧在于:相同的代码用于存储和恢复。这有两个积极的效果
- 用户只需在单个数据传递代码中定义两个方向的转换。
- 只需要存储纯数据。不需要额外的信息,例如数据类型、出现的顺序或如何解释 (例如 XML 序列化)。
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
在 ComplexConverter 内部
ComplexConverter 在设置或转换代码更改时会出现问题。更改后,它将读取不再匹配的旧数据。
为此,RestoreFromString()
在两种不同的模式下两次触发 Convert 事件:TryRestore 模式和 Restore 模式
TryRestore 模拟恢复,但将值写入缓冲区。如果发生错误,则捕获异常,并且恢复过程将中止,而不会将数据写入任何目标属性。因此,目标将保持其默认配置。
最后,在 Restore 模式下,数据将被复制到目标属性。