65.9K
CodeProject 正在变化。 阅读更多。
Home

ComplexConverter - 使配置更加灵活

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.80/5 (3投票s)

2008年4月17日

CPOL

2分钟阅读

viewsIcon

15008

downloadIcon

54

将复杂的对象结构转换为字符串并反向转换。将结果存储在应用程序设置中可以显著提高配置的灵活性

下载 ComplexConverter_src - 18.74 KB

引言

我想分享的是一种技术,可以收集多个数据项并将它们转换成一个字符串。该字符串可以存储为应用程序设置,并且所有数据都可以通过一次调用从该设置中恢复。
这对于持久化任意用户设置、窗体边界、窗体窗口状态、分隔面板的宽度、滑块位置、列标题的宽度等非常有用。
实际上,一个完整的 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 序列化)。
这些附加信息不需要存储,而是永久保留在 Convert 事件的处理程序代码中。因此,存储的字符串非常紧凑 - 例如上述代码示例的结果
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 模式下,数据将被复制到目标属性。

© . All rights reserved.