保存和加载 ListView 控件的全部内容






4.22/5 (8投票s)
有时您需要将 ListView 的内容保存到用户的本地文件系统中。使用 .Net Framework 2.0 中的 XmlTextWriter 和 Reader 可以轻松管理这一点。
引言
如果您想保存 ListView 控件的内容,有许多可能性。您可以序列化所有项目,您可以遍历所有项目并将它们保存到 .ini 文件,或者您决定编写一个 XML (可扩展标记语言) 文件
。
在我看来,最后一个选项是最好的。您可以控制所有操作,因此该功能 `非常灵活且可修改`。
背景
我曾经需要保存 ListView 中的标题、组以及它们包含的所有项目。直到那时,我一直使用 `序列化` 函数,因为它使用简单。但这只保存了项目,而没有包含项目的组。所以我编写了自己的方法来保存和加载 ListView 控件的 `整个` 内容。
代码背后的逻辑
好了,我们决定编写一个 `XML 配置文件`。
这很简单,我们将遍历所有标题、组及其项目。
这意味着:
有几个 `XML 节点`
- 文档以 `<ListView>` 开始
- 然后一个名为 `<Columns>` 的节点为每个 ColumnHeader 包含一个“Single-Line-Node”;它们保存其最重要的数据(文本、宽度和文本对齐方式)。
- 标签 `<Groups>` 包含每个组的一个节点
- 在包含名称和标题对齐方式的 `<Group>` 标签内,每个项目都有另一个节点来包含其子项。
好吧, 但那到底是什么意思?
当您这样保存 ListView 时…
… 输出配置文件如下所示
带组的 ListView 与不带组的 ListView 不同!
现在,知道了我的函数如何工作,您必须区分使用组的 ListView 和不使用组的 ListView。
为什么?
也许您已经注意到该函数首先遍历每个组,然后遍历其项目。但是,如果 ListView 没有组会怎样?因为没有组可以搜索其项目,所以找不到任何内容。因此,配置文件 `将为空`。
因此,我们必须编写 `四个不同的工作函数`(两个用于保存;两个用于加载)。它们的区别在于,不带组的 ListView 的函数直接处理项目并跳过组,因为它们不存在。
最后,我们来谈谈代码。
ListViewContent 类
现在您知道了函数背后的逻辑,以及使用组的 ListView 和不使用组的 ListView 之间存在显著差异。因此,我们可以考虑文件是如何编写的。
我们可以使用 `System.Xml` 命名空间中的两个类
- XmlDocument
XmlTextWriter
我选择了 `XmlTextWriter`,因为它比 XmlDocument 稍微容易处理一些。
让我们开始编写保存带组的 ListView 内容的函数。
ListViewWithGroupsSave
代码已经注释了,但我想补充一点。这并不难理解。该函数使用 `System.Xml` 命名空间中的 `XmlTextWriter`。然后它只是遍历我们想要保存的所有对象(标题、组、项目等)。
参数
-
lsv
:这是将要保存的 ListView 控件 destinationPath
:您要将配置文件保存到的路径。
重要提示:您必须指定一个文件扩展名,例如 .xml、.txt、.cfg 或您想要的任何内容。
返回值
如果没有发生错误,该函数将返回一个空字符串,否则返回 Exception 的消息。
''' <summary>
''' Saves the content of a ListView control which has no groups
''' </summary>
''' <param name="lsv">The ListView to be saved</param>
''' <param name="destinationPath">The path you want to save the config file to</param>
''' <returns>If no errors occur the function returns an empty String, otherwise the Exception's Message</returns>
Private Shared Function ListViewWithGroupsSave(ByVal lsv As ListView, ByVal destinationPath As String) As String
Try
' Set the encoding to Unicode
Dim enc As New UnicodeEncoding
' Declare a XmlTextWriter-Object, with which we are going to write the config file
Dim XMLobj As XmlTextWriter = New XmlTextWriter(destinationPath, enc)
With XMLobj
.Formatting = Formatting.Indented
.Indentation = 4
' Open the document
.WriteStartDocument()
' Start the document with a node named ListView, in which will be contained all the data
.WriteStartElement("ListView") ' <ListView>
' The node <Columns> will contain the ColumnHeaders of the ListView
.WriteStartElement("Columns") ' <Columns>
' Go through all Columns in the given ListView Control and write them into the document
For i As Integer = 0 To lsv.Columns.Count - 1
.WriteStartElement("Column") ' <Column
.WriteAttributeString("text", lsv.Columns(i).Text) ' text="xyz"
.WriteAttributeString("width", lsv.Columns(i).Width.ToString) ' width="xyz"
.WriteAttributeString("textAlign", lsv.Columns(i).TextAlign.ToString) ' textAlign="xyz"
.WriteEndElement() ' />
Next
' Complete the Columns-Tag
.WriteEndElement() ' </Columns>
' The node <Groups> will contain all Groups of The ListView
.WriteStartElement("Groups")
For j As Integer = 0 To lsv.Groups.Count - 1
' <Group
.WriteStartElement("Group")
.WriteAttributeString("name", lsv.Groups(j).Header)
' name="xyz"
.WriteAttributeString("headerAlignment", lsv.Groups(j).HeaderAlignment.ToString)
' headerAlignment="xyz">
' Loop through the Items of the given ListView...
For k As Integer = 0 To lsv.Groups(j).Items.Count - 1
.WriteStartElement("item") ' <item
.WriteAttributeString("text", lsv.Groups(j).Items(k).Text) ' text="xyz"
' ...and SubItems to write them to the Document
For l As Integer = 0 To lsv.Groups(j).Items(k).SubItems.Count - 2
.WriteStartElement("subItem") ' <subItem
.WriteAttributeString("text", lsv.Groups(j).Items(k).SubItems(l + 1).Text) ' text="xyz"
.WriteEndElement() ' />
Next
' At the end of each passage in this loop, the Item-Tag will be ended to start with a new item
.WriteEndElement() ' </item>
Next
' At the end of each passage in this loop, the Group-Tag will be ended to start with a new group
.WriteEndElement() ' </Group>
Next
' Complete the Groups-Tag to signalize that all groups have been written to the document
.WriteEndElement() '</Groups>
' Close the ListView-Tag, now we're almost done
.WriteEndElement() ' </ListView>
' Finally, complete the document and close the XmlTextWriter-Object
.WriteEndDocument()
.Close()
'_______________________________________________________________________
' |
' For instance, a configuration file is set up like this: |
'-----------------------------------------------------------------------|
' <ListView> |
' <Columns> |
' <Column text=“Column 1“ width=“100“ textAlign=“Left“ /> |
' <Column text=“Column 2“ width=“100“ textAlign=“Left“ /> |
' <Column text=“Column 3“ width=“100“ textAlign=“Left“ /> |
' <Column text=“Column 4“ width=“100“ textAlign=“Left“ /> |
' <Column text=“Column 5“ width=“100“ textAlign=“Left“ /> |
' <Column text=“Column 6“ width=“100“ textAlign=“Left“ /> |
' <Column text=“Column 7“ width=“100“ textAlign=“Left“ /> |
' </Columns> |
' |
' <Groups> |
' <Group name=“Group“ headerAlignment=“Left“> |
' <item text=“Description“> |
' <subItem text=“Text 0“ /> |
' <subItem text=“Text 1“ /> |
' <subItem text=“Text 2“ /> |
' <subItem text=“Text 3“ /> |
' <subItem text=“Text 4“ /> |
' <subItem text=“Text 5“ /> |
' </item> |
' </Group> |
' </Groups> |
' </ListView> |
'_______________________________________________________________________|
End With
Return String.Empty ' If this function worked faultless, an emtpy String will bei returned
Catch ex As Exception
Return ex.Message ' If an error occurs during some of the operation, the exception's message will be returned
End Try
End Function
ListViewWithGroupsLoad
在加载函数中,我们使用 `XmlTextReader`。它并不比保存函数复杂多少,但我将简要解释一下。有一个循环,直到 Reader 到达已保存文档的末尾。在每次迭代中,都会查询当前元素是什么。因此,逐步读取所有值并将其添加到 ListView。
参数:
lsv
:这是将要加载的 ListView 控件loadedPath
:用户文件系统上配置文件的路径append
:“True”如果您想将保存的内容追加到 ListView。“False”如果您想在加载前清除 ListView。loadColumns
:指示您是否要加载 HeaderColumn。addInvisibleColumn
:您是否想向 ListView 添加一个“不可见”列?也许您已经注意到无法对第一个 ColumnHeader 的文本进行对齐。所以这个选项非常有帮助。通过一个宽度为 0 像素的“不可见”第一列,您可以做到这一点。但不要忘记在 ListView.ColumnWidthChanging 事件中将其宽度设置为 0 像素以“锁定”它。
返回值
如果没有发生错误,该函数将返回一个空字符串,否则返回 Exception 的消息。
''' <summary>
''' Loads the whole content of a ListView with groups from a file
''' </summary>
''' <param name="lsv">The ListView to be loaded</param>
''' <param name="loadedPath">The path to the configuration file</param>
''' <param name="append">"True" if you want to append the saved content to the ListView. "False" if you want the ListView to be cleared before the loading begins</param>
''' <param name="loadColumns">Indicates if the HeaderColumns should be loaded</param>
''' <param name="addInVisibleColumn">"True" if you want that an "invisible" column will be added to the ListView.</param>
''' <returns>If no errors occur the function returns an empty String, otherwise the Exception's Message</returns>
Private Shared Function ListViewWithGroupsLoad(ByVal lsv As ListView, ByVal loadedPath As String, Optional ByVal append As Boolean = False,
Optional ByVal loadColumns As Boolean = True, Optional ByVal addInVisibleColumn As Boolean = False) As String
Try
' If the user wants to, the ListView and its Columns are cleared, and an invisible Column will be insered to the top (index 0)
If Not append Then lsv.Clear()
If loadColumns Then lsv.Columns.Clear()
If addInVisibleColumn Then lsv.Columns.Insert(0, "", 0)
' We need an XMLReader for reading the given configuration file
Dim XMLReader As XmlReader = New XmlTextReader(loadedPath)
' Declare the ListViewItem, to wich the properties (text, SubItems) will be assigned
Dim grp As New ListViewGroup
Dim listItem As ListViewItem = New ListViewItem
' Now we're going to read the file
With XMLReader
' As long as the reader hasn't come to the end of the document, this loop is executed
Do While .Read
' If the user wants to the Columns, read in their properties
If loadColumns Then
' The tag <Columns> was found; it contains the HeaderColumns of the saved ListView
If .Name = "Column" Then
Dim align As HorizontalAlignment = 0
' Convert the saved textAlign-String to a HorizontalAlignment
Select Case .GetAttribute("textAlign").ToLower
Case "left"
align = HorizontalAlignment.Left
Case "right"
align = HorizontalAlignment.Right
Case "center"
align = HorizontalAlignment.Center
End Select
' Add the columns with the loaded properties (text, width, text alignment)
lsv.Columns.Add(.GetAttribute("text"), Convert.ToInt32(.GetAttribute("width")), align)
End If
End If
' The tag <Group> was found; it contains the name of the group and its text alignment
If .Name = "Group" Then
'If the Tag is a StartElement (<Group>, and not </Group>), the ListView is added all the saved Groups
If .IsStartElement Then
Dim align As HorizontalAlignment = 0
' Convert the saved textAlign-String to a HorizontalAlignment
Select Case .GetAttribute("headerAlignment").ToLower
Case "left"
align = HorizontalAlignment.Left
Case "right"
align = HorizontalAlignment.Right
Case "center"
align = HorizontalAlignment.Center
End Select
' Set the required properties of the ListViewGroup (name and text alignment)
grp = New ListViewGroup(.GetAttribute("name"), align)
' Add the group to the ListView
lsv.Groups.Add(grp)
End If
End If
' The item-Tag was found; it contains the text of each Item and their SubItems
If .Name = "item" Then
' If the Tag is a StartElement (<item>; and not </item>), assign the property "text" to the ListViewItem
If .IsStartElement Then
If addInVisibleColumn Then
listItem.SubItems.Add(.GetAttribute("text"))
Else
listItem.Text = .GetAttribute("text")
End If
Else
' Otherwise, if it's an EndTag (</item>), the previously filled ListViewItem is added to the ListView
listItem.Group = grp
lsv.Items.Add(listItem)
listItem = New ListViewItem
End If
End If
' The Element <subItem> was found; it contains the SubItem's text
If .Name = "subItem" Then
' The ListView is added a SubItem with the attribute "text" from the element <subItem>
listItem.SubItems.Add(.GetAttribute("text"))
End If
Loop
.Close() ' Close the XMLTextReader
End With
' Name all Groups as like their Headers
For i As Integer = 0 To lsv.Groups.Count - 1
lsv.Groups(i).Name = lsv.Groups(i).Header
Next
Return String.Empty ' If this function worked faultless, an emtpy String will bei returned
Catch ex As Exception
Return ex.Message ' If an error occurs during some of the operation, the exception's message will be returned
End Try
End Function
ListViewWithNoGroupsSave
该函数也 `无需解释`,因为它 `与 ListViewWithNoGroupsSave 几乎相同`。不同之处在于这里 `没有循环遍历组`;我们直接进入项目。
参数和返回值 `与` 其他函数 `相同`。
''' <summary>
''' Saves the content of a ListView control which has groups
''' </summary>
''' <param name="lsv">The ListView to be saved</param>
''' <param name="destinationPath">The path you want to save the config file to</param>
''' <returns>If no errors occur the function returns an empty String, otherwise the Exception's Message</returns>
Private Shared Function ListViewWithNoGroupsSave(ByVal lsv As ListView, ByVal destinationPath As String) As String
Try
' Set the encoding to Unicode
Dim enc As New UnicodeEncoding
' Declare a XmlTextWriter-Object, with which we are going to write the config file
Dim XMLobj As XmlTextWriter = New XmlTextWriter(destinationPath, enc)
With XMLobj
.Formatting = Formatting.Indented
.Indentation = 4
' Open the document
.WriteStartDocument()
' Start the document with a node named ListView, in which will be contained all the data
.WriteStartElement("ListView") ' <ListView>
' The node <Columns> will contain the ColumnHeaders of the ListView
.WriteStartElement("Columns") ' <Columns>
' Go through all Columns in the given ListView Control and write them into the document
For i As Integer = 0 To lsv.Columns.Count - 1
.WriteStartElement("Column") ' <Column
.WriteAttributeString("text", lsv.Columns(i).Text) ' text="xyz"
.WriteAttributeString("width", lsv.Columns(i).Width.ToString) ' width="xyz"
.WriteAttributeString("textAlign", lsv.Columns(i).TextAlign.ToString) ' textAlign="xyz"
.WriteEndElement() ' />
Next
' Complete the Columns-Tag
.WriteEndElement() ' </Columns>
' The node <Items> will contain all Items of the ListView
.WriteStartElement("Items") ' <Items>
' Loop through the given ListView's items...
For k As Integer = 0 To lsv.Items.Count - 1
.WriteStartElement("item") ' <item
.WriteAttributeString("text", lsv.Items(k).Text) ' text="xyz">
' ...and SubItems to write them to the Document
For l As Integer = 0 To lsv.Items(k).SubItems.Count - 2
.WriteStartElement("subItem") ' <subItem
.WriteAttributeString("text", lsv.Items(k).SubItems(l + 1).Text) ' text="xyz"
.WriteEndElement() ' />
Next
' At the end of each passage in the main loop, the Item-Tag will be ended to start with a new item
.WriteEndElement() ' </item>
Next
' Complete the Items-Tag to signalize that all items have been written to the document
.WriteEndElement() ' </Items>
' Close the ListView-Tag, now we're almost done
.WriteEndElement() ' </ListView>
' Finally, complete the document and close the XmlTextWriter-Object
.WriteEndDocument()
.Close()
'_______________________________________________________________________
' |
' For instance, a configuration file is set up like this: |
'-----------------------------------------------------------------------|
' <ListView> |
' <Columns> |
' <Column text=“Column 1“ width=“100“ textAlign=“Left“ /> |
' <Column text=“Column 2“ width=“100“ textAlign=“Left“ /> |
' <Column text=“Column 3“ width=“100“ textAlign=“Left“ /> |
' <Column text=“Column 4“ width=“100“ textAlign=“Left“ /> |
' <Column text=“Column 5“ width=“100“ textAlign=“Left“ /> |
' <Column text=“Column 6“ width=“100“ textAlign=“Left“ /> |
' <Column text=“Column 7“ width=“100“ textAlign=“Left“ /> |
' </Columns> |
' |
' <Groups> |
' <Group name=“Group“ headerAlignment=“Left“> |
' <item text=“Description“> |
' <subItem text=“Text 0“ /> |
' <subItem text=“Text 1“ /> |
' <subItem text=“Text 2“ /> |
' <subItem text=“Text 3“ /> |
' <subItem text=“Text 4“ /> |
' <subItem text=“Text 5“ /> |
' </item> |
' </Group> |
' </Groups> |
' </ListView> |
'_______________________________________________________________________|
End With
Return String.Empty ' If this function worked faultless, an emtpy String will bei returned
Catch ex As Exception
Return ex.Message ' If an error occurs during some of the operation, the exception's message will be returned
End Try
End Function
ListViewWithNoGroupsLoad
该函数也 `跳过` 了 <Group> 标签,因为它们不存在。
参数和返回值 `与` 其他函数 `相同`。
''' <summary>
''' Loads the whole content of a ListView without groups from a file
''' </summary>
''' <param name="lsv">The ListView to be loaded</param>
''' <param name="loadedPath">The path to the configuration file</param>
''' <param name="append">"True" if you want to append the saved content to the ListView. "False" if you want the ListView to be cleared before the loading begins</param>
''' <param name="loadColumns">Indicates if the HeaderColumns should be loaded</param>
''' <param name="addInVisibleColumn">"True" if you want that an "invisible" column will be added to the ListView.</param>
''' <returns>If no errors occur the function returns an empty String, otherwise the Exception's Message</returns>
Private Shared Function ListViewWithNoGroupsLoad(ByVal lsv As ListView, ByVal loadedPath As String, Optional ByVal append As Boolean = False,
Optional ByVal loadColumns As Boolean = True, Optional ByVal addInVisibleColumn As Boolean = False) As String
Try
' If the user wants to, the ListView and its Columns are cleared, and an invisible Column will be insered to the top (index 0)
If Not append Then lsv.Clear()
If loadColumns Then lsv.Columns.Clear()
If addInVisibleColumn Then lsv.Columns.Insert(0, "", 0)
' We need an XMLReader for reading the given configuration file
Dim XMLReader As XmlReader = New XmlTextReader(loadedPath)
' Declare the ListViewItem, to wich the properties (text, SubItems) will be assigned
Dim listItem As ListViewItem = New ListViewItem
' Now we're going to read the file
With XMLReader
' As long as the reader hasn't come to the end of the document, this loop is executed
Do While .Read
' If the user wants to the Columns, read in their properties
If loadColumns Then
' The tag <Columns> was found; it contains the HeaderColumns of the saved ListView
If .Name = "Column" Then
Dim align As HorizontalAlignment = 0
' Convert the saved textAlign-String to a HorizontalAlignment
Select Case .GetAttribute("textAlign").ToLower
Case "left"
align = HorizontalAlignment.Left
Case "right"
align = HorizontalAlignment.Right
Case "center"
align = HorizontalAlignment.Center
End Select
' Add the columns with the loaded properties (text, width, text alignment)
lsv.Columns.Add(.GetAttribute("text"), Convert.ToInt32(.GetAttribute("width")), align)
End If
End If
' The item-Tag was found; it contains the text of each Item and their SubItems
If .Name = "item" Then
' If the Tag is a StartElement (<item>; and not </item>), assign the property "text" to the ListViewItem
If .IsStartElement Then
If addInVisibleColumn Then
listItem.SubItems.Add(.GetAttribute("text"))
Else
listItem.Text = .GetAttribute("text")
End If
Else
' Otherwise, if it's an EndTag (</item>), the previously filled ListViewItem is added to the ListView
lsv.Items.Add(listItem)
listItem = New ListViewItem
End If
End If
' The Element <subItem> was found; it contains the SubItem's text
If .Name = "subItem" Then
' The ListView is added a SubItem with the attribute "text" from the element <subItem>
listItem.SubItems.Add(.GetAttribute("text"))
End If
Loop
.Close() ' Close the XMLTextReader
End With
' Name all Groups as like their Headers
For i As Integer = 0 To lsv.Groups.Count - 1
lsv.Groups(i).Name = lsv.Groups(i).Header
Next
Return String.Empty ' If this function worked faultless, an emtpy String will bei returned
Catch ex As Exception
Return ex.Message ' If an error occurs during some of the operation, the exception's message will be returned
End Try
End Function
我们必须确定要保存的 ListView 是否有组!
既然我们现在知道如何处理带有组的 ListView 和不带组的 ListView,我们必须 `检测用户想要保存的 ListView 类型`。
好了,我们现在在“`ListViewContent`”类中有四个函数。确保它们被声明为“`Private Shared`”,而类本身被声明为“`Public MustInherit`”。
为什么?我稍后会在“使用代码”中解释。
现在我们需要 `两个 public shared 函数`供用户调用。在这两个函数中,我们不保存或加载任何内容,我们只是检测 ListView 控件是否有组。完成此操作后,我们将调用正确的函数。
保存
那么,我们如何检测要保存的 ListView 的类型呢?
我们可以访问 ListView 控件所需的所有信息。因此,包含某些组的 ListView 中的组数大于零。但我们也必须检查 ListView 是否显示任何组。
将其放入 Visual Basic .Net 并不难
''' <summary>
''' Saves the content of a ListView control which has no groups
''' </summary>
''' <param name="lsv">The ListView to be saved</param>
''' <param name="destinationPath">The path you want to save the config file to</param>
''' <returns>If no errors occur the function returns an empty String, otherwise the Exception's Message</returns>
Public Shared Function Save(ByVal lsv As ListView, ByVal destinationPath As String) As String
If lsv.Groups.Count > 0 AndAlso lsv.ShowGroups Then
Return ListViewWithGroupsSave(lsv, destinationPath)
Else
Return ListViewWithNoGroupsSave(lsv, destinationPath)
End If
End Function
如果给定的 ListView 包含的组数大于零并且它也显示了它们,则该函数调用 `ListViewWithGroupsSave`,否则调用 `ListViewWithNoGroupsSave`。
然后返回返回值。如果没有发生错误,它将是一个空字符串,否则是 Exception 的消息。
Load (加载)
要识别要加载的文件是否包含组,会稍微困难一些。我们需要两个命名空间
System.IO
-
System.Text.RegularExpressions
首先,我们需要已保存配置文件内容。我们使用 `System.IO` 命名空间中的 `File.ReadAllText` 函数并删除所有换行符。
为了找出文件是否包含 `<group>` 标签,我们需要 `System.Text.RegularExpressions` 命名空间中的 `RegEx.IsMatch` 函数。Regex 是验证和编辑字符串的强大工具。`IsMatch` 会根据模式检查字符串的有效性。
这是我们将使用的非常简单的模式: `(<groups>.*</groups>)|(<groups />)`
- 括号用于对两个不同的表达式进行分组
- 此模式搜索标签 <groups>…
- “**。**”代表任何字符,量词“**\***”表示前面的表达式(“**。**”=任何字符)可以出现任意次数。
- 然后用 </groups> 关闭组标签
- 管道符号(**|**)查找符号**|**之前或之后的表达式。
- 此模式还查找空组 (<groups />)
由于使用了管道符号,如果找到 `<groups>.*</groups> 或 <groups />` 中的 `任何一个`,此函数将返回“True”。
这样我们就得到了 VB 代码
''' <summary>
''' Loads the whole content of a ListView
''' </summary>
''' <param name="lsv">The ListView to be loaded</param>
''' <param name="loadedPath">The path to the configuration file</param>
''' <param name="append">"True" if you want to append the saved content to the ListView. "False" if you want the ListView to be cleared before the loading begins</param>
''' <param name="loadColumns">Indicates if the HeaderColumns should be loaded</param>
''' <param name="addInVisibleColumn">"True" if you want that an "invisible" column will be added to the ListView.</param>
''' <returns>If no errors occur the function returns an empty String, otherwise the Exception's Message</returns>
Public Shared Function Load(ByVal lsv As ListView, ByVal loadedPath As String, Optional ByVal append As Boolean = False,
Optional ByVal loadColumns As Boolean = True, Optional ByVal addInVisibleColumn As Boolean = False) As String
Dim hasFileGroups As Boolean =
Regex.IsMatch(File.ReadAllText(loadedPath).Replace(Chr(10), ""), "(<groups>.*</groups>)|(<groups />)",
RegexOptions.IgnoreCase)
If hasFileGroups Then
Return ListViewWithGroupsLoad(lsv, loadedPath, append, loadColumns, addInVisibleColumn)
Else
Return ListViewWithNoGroupsLoad(lsv, loadedPath, append, loadColumns, addInVisibleColumn)
End If
End Function
如果已保存的配置文件 `包含 <group>` 标签,则该函数调用 `ListViewWithGroupsLoad`,否则调用 `ListViewWithNoGroupsLoad`。
然后返回返回值。如果没有发生错误,它将是一个空字符串,否则是 Exception 的消息。
整个类
这是“ListViewContent”类的完整代码
Option Strict On
Imports System.Text
Imports System.Xml
Imports System.IO
Imports System.Text.RegularExpressions
Public MustInherit Class ListViewContent
#Region "Private Methods for saving and loading the content of a ListView"
#Region "Save the whole content of a ListView with groups into a file"
''' <summary>
''' Saves the content of a ListView control which has no groups
''' </summary>
''' <param name="lsv">The ListView to be saved</param>
''' <param name="destinationPath">The path you want to save the config file to</param>
''' <returns>If no errors occur the function returns an empty String, otherwise the Exception's Message</returns>
Private Shared Function ListViewWithGroupsSave(ByVal lsv As ListView, ByVal destinationPath As String) As String
Try
' Set the encoding to Unicode
Dim enc As New UnicodeEncoding
' Declare a XmlTextWriter-Object, with which we are going to write the config file
Dim XMLobj As XmlTextWriter = New XmlTextWriter(destinationPath, enc)
With XMLobj
.Formatting = Formatting.Indented
.Indentation = 4
' Open the document
.WriteStartDocument()
' Start the document with a node named ListView, in which will be contained all the data
.WriteStartElement("ListView") ' <ListView>
' The node <Columns> will contain the ColumnHeaders of the ListView
.WriteStartElement("Columns") ' <Columns>
' Go through all Columns in the given ListView Control and write them into the document
For i As Integer = 0 To lsv.Columns.Count - 1
.WriteStartElement("Column") ' <Column
.WriteAttributeString("text", lsv.Columns(i).Text) ' text="xyz"
.WriteAttributeString("width", lsv.Columns(i).Width.ToString) ' width="xyz"
.WriteAttributeString("textAlign", lsv.Columns(i).TextAlign.ToString) ' textAlign="xyz"
.WriteEndElement() ' />
Next
' Complete the Columns-Tag
.WriteEndElement() ' </Columns>
' The node <Groups> will contain all Groups of The ListView
.WriteStartElement("Groups")
For j As Integer = 0 To lsv.Groups.Count - 1
' <Group
.WriteStartElement("Group")
.WriteAttributeString("name", lsv.Groups(j).Header)
' name="xyz"
.WriteAttributeString("headerAlignment", lsv.Groups(j).HeaderAlignment.ToString)
' headerAlignment="xyz">
' Loop through the Items of the given ListView...
For k As Integer = 0 To lsv.Groups(j).Items.Count - 1
.WriteStartElement("item") ' <item
.WriteAttributeString("text", lsv.Groups(j).Items(k).Text) ' text="xyz"
' ...and SubItems to write them to the Document
For l As Integer = 0 To lsv.Groups(j).Items(k).SubItems.Count - 2
.WriteStartElement("subItem") ' <subItem
.WriteAttributeString("text", lsv.Groups(j).Items(k).SubItems(l + 1).Text) ' text="xyz"
.WriteEndElement() ' />
Next
' At the end of each passage in this loop, the Item-Tag will be ended to start with a new item
.WriteEndElement() ' </item>
Next
' At the end of each passage in this loop, the Group-Tag will be ended to start with a new group
.WriteEndElement() ' </Group>
Next
' Complete the Groups-Tag to signalize that all groups have been written to the document
.WriteEndElement() '</Groups>
' Close the ListView-Tag, now we're almost done
.WriteEndElement() ' </ListView>
' Finally, complete the document and close the XmlTextWriter-Object
.WriteEndDocument()
.Close()
'_______________________________________________________________________
' |
' For instance, a configuration file is set up like this: |
'-----------------------------------------------------------------------|
' <ListView> |
' <Columns> |
' <Column text=“Column 1“ width=“100“ textAlign=“Left“ /> |
' <Column text=“Column 2“ width=“100“ textAlign=“Left“ /> |
' <Column text=“Column 3“ width=“100“ textAlign=“Left“ /> |
' <Column text=“Column 4“ width=“100“ textAlign=“Left“ /> |
' <Column text=“Column 5“ width=“100“ textAlign=“Left“ /> |
' <Column text=“Column 6“ width=“100“ textAlign=“Left“ /> |
' <Column text=“Column 7“ width=“100“ textAlign=“Left“ /> |
' </Columns> |
' |
' <Groups> |
' <Group name=“Group“ headerAlignment=“Left“> |
' <item text=“Description“> |
' <subItem text=“Text 0“ /> |
' <subItem text=“Text 1“ /> |
' <subItem text=“Text 2“ /> |
' <subItem text=“Text 3“ /> |
' <subItem text=“Text 4“ /> |
' <subItem text=“Text 5“ /> |
' </item> |
' </Group> |
' </Groups> |
' </ListView> |
'_______________________________________________________________________|
End With
Return String.Empty ' If this function worked faultless, an emtpy String will bei returned
Catch ex As Exception
Return ex.Message ' If an error occurs during some of the operation, the exception's message will be returned
End Try
End Function
#End Region
#Region "Load the whole content of a ListView with groups from a file"
''' <summary>
''' Loads the whole content of a ListView with groups from a file
''' </summary>
''' <param name="lsv">The ListView to be loaded</param>
''' <param name="loadedPath">The path to the configuration file</param>
''' <param name="append">"True" if you want to append the saved content to the ListView. "False" if you want the ListView to be cleared before the loading begins</param>
''' <param name="loadColumns">Indicates if the HeaderColumns should be loaded</param>
''' <param name="addInVisibleColumn">"True" if you want that an "invisible" column will be added to the ListView.</param>
''' <returns>If no errors occur the function returns an empty String, otherwise the Exception's Message</returns>
Private Shared Function ListViewWithGroupsLoad(ByVal lsv As ListView, ByVal loadedPath As String, Optional ByVal append As Boolean = False,
Optional ByVal loadColumns As Boolean = True, Optional ByVal addInVisibleColumn As Boolean = False) As String
Try
' If the user wants to, the ListView and its Columns are cleared, and an invisible Column will be insered to the top (index 0)
If Not append Then lsv.Clear()
If loadColumns Then lsv.Columns.Clear()
If addInVisibleColumn Then lsv.Columns.Insert(0, "", 0)
' We need an XMLReader for reading the given configuration file
Dim XMLReader As XmlReader = New XmlTextReader(loadedPath)
' Declare the ListViewItem, to wich the properties (text, SubItems) will be assigned
Dim grp As New ListViewGroup
Dim listItem As ListViewItem = New ListViewItem
' Now we're going to read the file
With XMLReader
' As long as the reader hasn't come to the end of the document, this loop is executed
Do While .Read
' If the user wants to the Columns, read in their properties
If loadColumns Then
' The tag <Columns> was found; it contains the HeaderColumns of the saved ListView
If .Name = "Column" Then
Dim align As HorizontalAlignment = 0
' Convert the saved textAlign-String to a HorizontalAlignment
Select Case .GetAttribute("textAlign").ToLower
Case "left"
align = HorizontalAlignment.Left
Case "right"
align = HorizontalAlignment.Right
Case "center"
align = HorizontalAlignment.Center
End Select
' Add the columns with the loaded properties (text, width, text alignment)
lsv.Columns.Add(.GetAttribute("text"), Convert.ToInt32(.GetAttribute("width")), align)
End If
End If
' The tag <Group> was found; it contains the name of the group and its text alignment
If .Name = "Group" Then
'If the Tag is a StartElement (<Group>, and not </Group>), the ListView is added all the saved Groups
If .IsStartElement Then
Dim align As HorizontalAlignment = 0
' Convert the saved textAlign-String to a HorizontalAlignment
Select Case .GetAttribute("headerAlignment").ToLower
Case "left"
align = HorizontalAlignment.Left
Case "right"
align = HorizontalAlignment.Right
Case "center"
align = HorizontalAlignment.Center
End Select
' Set the required properties of the ListViewGroup (name and text alignment)
grp = New ListViewGroup(.GetAttribute("name"), align)
' Add the group to the ListView
lsv.Groups.Add(grp)
End If
End If
' The item-Tag was found; it contains the text of each Item and their SubItems
If .Name = "item" Then
' If the Tag is a StartElement (<item>; and not </item>), assign the property "text" to the ListViewItem
If .IsStartElement Then
If addInVisibleColumn Then
listItem.SubItems.Add(.GetAttribute("text"))
Else
listItem.Text = .GetAttribute("text")
End If
Else
' Otherwise, if it's an EndTag (</item>), the previously filled ListViewItem is added to the ListView
listItem.Group = grp
lsv.Items.Add(listItem)
listItem = New ListViewItem
End If
End If
' The Element <subItem> was found; it contains the SubItem's text
If .Name = "subItem" Then
' The ListView is added a SubItem with the attribute "text" from the element <subItem>
listItem.SubItems.Add(.GetAttribute("text"))
End If
Loop
.Close() ' Close the XMLTextReader
End With
' Name all Groups as like their Headers
For i As Integer = 0 To lsv.Groups.Count - 1
lsv.Groups(i).Name = lsv.Groups(i).Header
Next
Return String.Empty ' If this function worked faultless, an emtpy String will bei returned
Catch ex As Exception
Return ex.Message ' If an error occurs during some of the operation, the exception's message will be returned
End Try
End Function
#End Region
#Region "Save the whole content of a ListView without groups into a file"
''' <summary>
''' Saves the content of a ListView control which has groups
''' </summary>
''' <param name="lsv">The ListView to be saved</param>
''' <param name="destinationPath">The path you want to save the config file to</param>
''' <returns>If no errors occur the function returns an empty String, otherwise the Exception's Message</returns>
Private Shared Function ListViewWithNoGroupsSave(ByVal lsv As ListView, ByVal destinationPath As String) As String
Try
' Set the encoding to Unicode
Dim enc As New UnicodeEncoding
' Declare a XmlTextWriter-Object, with which we are going to write the config file
Dim XMLobj As XmlTextWriter = New XmlTextWriter(destinationPath, enc)
With XMLobj
.Formatting = Formatting.Indented
.Indentation = 4
' Open the document
.WriteStartDocument()
' Start the document with a node named ListView, in which will be contained all the data
.WriteStartElement("ListView") ' <ListView>
' The node <Columns> will contain the ColumnHeaders of the ListView
.WriteStartElement("Columns") ' <Columns>
' Go through all Columns in the given ListView Control and write them into the document
For i As Integer = 0 To lsv.Columns.Count - 1
.WriteStartElement("Column") ' <Column
.WriteAttributeString("text", lsv.Columns(i).Text) ' text="xyz"
.WriteAttributeString("width", lsv.Columns(i).Width.ToString) ' width="xyz"
.WriteAttributeString("textAlign", lsv.Columns(i).TextAlign.ToString) ' textAlign="xyz"
.WriteEndElement() ' />
Next
' Complete the Columns-Tag
.WriteEndElement() ' </Columns>
' The node <Items> will contain all Items of the ListView
.WriteStartElement("Items") ' <Items>
' Loop through the given ListView's items...
For k As Integer = 0 To lsv.Items.Count - 1
.WriteStartElement("item") ' <item
.WriteAttributeString("text", lsv.Items(k).Text) ' text="xyz">
' ...and SubItems to write them to the Document
For l As Integer = 0 To lsv.Items(k).SubItems.Count - 2
.WriteStartElement("subItem") ' <subItem
.WriteAttributeString("text", lsv.Items(k).SubItems(l + 1).Text) ' text="xyz"
.WriteEndElement() ' />
Next
' At the end of each passage in the main loop, the Item-Tag will be ended to start with a new item
.WriteEndElement() ' </item>
Next
' Complete the Items-Tag to signalize that all items have been written to the document
.WriteEndElement() ' </Items>
' Close the ListView-Tag, now we're almost done
.WriteEndElement() ' </ListView>
' Finally, complete the document and close the XmlTextWriter-Object
.WriteEndDocument()
.Close()
'_______________________________________________________________________
' |
' For instance, a configuration file is set up like this: |
'-----------------------------------------------------------------------|
' <ListView> |
' <Columns> |
' <Column text=“Column 1“ width=“100“ textAlign=“Left“ /> |
' <Column text=“Column 2“ width=“100“ textAlign=“Left“ /> |
' <Column text=“Column 3“ width=“100“ textAlign=“Left“ /> |
' <Column text=“Column 4“ width=“100“ textAlign=“Left“ /> |
' <Column text=“Column 5“ width=“100“ textAlign=“Left“ /> |
' <Column text=“Column 6“ width=“100“ textAlign=“Left“ /> |
' <Column text=“Column 7“ width=“100“ textAlign=“Left“ /> |
' </Columns> |
' |
' <Groups> |
' <Group name=“Group“ headerAlignment=“Left“> |
' <item text=“Description“> |
' <subItem text=“Text 0“ /> |
' <subItem text=“Text 1“ /> |
' <subItem text=“Text 2“ /> |
' <subItem text=“Text 3“ /> |
' <subItem text=“Text 4“ /> |
' <subItem text=“Text 5“ /> |
' </item> |
' </Group> |
' </Groups> |
' </ListView> |
'_______________________________________________________________________|
End With
Return String.Empty ' If this function worked faultless, an emtpy String will bei returned
Catch ex As Exception
Return ex.Message ' If an error occurs during some of the operation, the exception's message will be returned
End Try
End Function
#End Region
#Region "Load the whole content of a ListView without groups from a file"
''' <summary>
''' Loads the whole content of a ListView without groups from a file
''' </summary>
''' <param name="lsv">The ListView to be loaded</param>
''' <param name="loadedPath">The path to the configuration file</param>
''' <param name="append">"True" if you want to append the saved content to the ListView. "False" if you want the ListView to be cleared before the loading begins</param>
''' <param name="loadColumns">Indicates if the HeaderColumns should be loaded</param>
''' <param name="addInVisibleColumn">"True" if you want that an "invisible" column will be added to the ListView.</param>
''' <returns>If no errors occur the function returns an empty String, otherwise the Exception's Message</returns>
Private Shared Function ListViewWithNoGroupsLoad(ByVal lsv As ListView, ByVal loadedPath As String, Optional ByVal append As Boolean = False,
Optional ByVal loadColumns As Boolean = True, Optional ByVal addInVisibleColumn As Boolean = False) As String
Try
' If the user wants to, the ListView and its Columns are cleared, and an invisible Column will be insered to the top (index 0)
If Not append Then lsv.Clear()
If loadColumns Then lsv.Columns.Clear()
If addInVisibleColumn Then lsv.Columns.Insert(0, "", 0)
' We need an XMLReader for reading the given configuration file
Dim XMLReader As XmlReader = New XmlTextReader(loadedPath)
' Declare the ListViewItem, to wich the properties (text, SubItems) will be assigned
Dim listItem As ListViewItem = New ListViewItem
' Now we're going to read the file
With XMLReader
' As long as the reader hasn't come to the end of the document, this loop is executed
Do While .Read
' If the user wants to the Columns, read in their properties
If loadColumns Then
' The tag <Columns> was found; it contains the HeaderColumns of the saved ListView
If .Name = "Column" Then
Dim align As HorizontalAlignment = 0
' Convert the saved textAlign-String to a HorizontalAlignment
Select Case .GetAttribute("textAlign").ToLower
Case "left"
align = HorizontalAlignment.Left
Case "right"
align = HorizontalAlignment.Right
Case "center"
align = HorizontalAlignment.Center
End Select
' Add the columns with the loaded properties (text, width, text alignment)
lsv.Columns.Add(.GetAttribute("text"), Convert.ToInt32(.GetAttribute("width")), align)
End If
End If
' The item-Tag was found; it contains the text of each Item and their SubItems
If .Name = "item" Then
' If the Tag is a StartElement (<item>; and not </item>), assign the property "text" to the ListViewItem
If .IsStartElement Then
If addInVisibleColumn Then
listItem.SubItems.Add(.GetAttribute("text"))
Else
listItem.Text = .GetAttribute("text")
End If
Else
' Otherwise, if it's an EndTag (</item>), the previously filled ListViewItem is added to the ListView
lsv.Items.Add(listItem)
listItem = New ListViewItem
End If
End If
' The Element <subItem> was found; it contains the SubItem's text
If .Name = "subItem" Then
' The ListView is added a SubItem with the attribute "text" from the element <subItem>
listItem.SubItems.Add(.GetAttribute("text"))
End If
Loop
.Close() ' Close the XMLTextReader
End With
' Name all Groups as like their Headers
For i As Integer = 0 To lsv.Groups.Count - 1
lsv.Groups(i).Name = lsv.Groups(i).Header
Next
Return String.Empty ' If this function worked faultless, an emtpy String will bei returned
Catch ex As Exception
Return ex.Message ' If an error occurs during some of the operation, the exception's message will be returned
End Try
End Function
#End Region
#End Region
''' <summary>
''' Saves the content of a ListView control which has no groups
''' </summary>
''' <param name="lsv">The ListView to be saved</param>
''' <param name="destinationPath">The path you want to save the config file to</param>
''' <returns>If no errors occur the function returns an empty String, otherwise the Exception's Message</returns>
Public Shared Function Save(ByVal lsv As ListView, ByVal destinationPath As String) As String
If lsv.Groups.Count > 0 AndAlso lsv.ShowGroups Then
Return ListViewWithGroupsSave(lsv, destinationPath)
Else
Return ListViewWithNoGroupsSave(lsv, destinationPath)
End If
End Function
''' <summary>
''' Loads the whole content of a ListView
''' </summary>
''' <param name="lsv">The ListView to be loaded</param>
''' <param name="loadedPath">The path to the configuration file</param>
''' <param name="append">"True" if you want to append the saved content to the ListView. "False" if you want the ListView to be cleared before the loading begins</param>
''' <param name="loadColumns">Indicates if the HeaderColumns should be loaded</param>
''' <param name="addInVisibleColumn">"True" if you want that an "invisible" column will be added to the ListView.</param>
''' <returns>If no errors occur the function returns an empty String, otherwise the Exception's Message</returns>
Public Shared Function Load(ByVal lsv As ListView, ByVal loadedPath As String, Optional ByVal append As Boolean = False,
Optional ByVal loadColumns As Boolean = True, Optional ByVal addInVisibleColumn As Boolean = False) As String
Dim hasFileGroups As Boolean =
Regex.IsMatch(File.ReadAllText(loadedPath).Replace(Chr(10), ""), "(<groups>.*</groups>)|(<groups />)",
RegexOptions.IgnoreCase)
If hasFileGroups Then
Return ListViewWithGroupsLoad(lsv, loadedPath, append, loadColumns, addInVisibleColumn)
Else
Return ListViewWithNoGroupsLoad(lsv, loadedPath, append, loadColumns, addInVisibleColumn)
End If
End Function
End Class
Using the Code
在“我们必须确定要保存的 ListView 是否有组!”中,我说过类中必须有 `四个 private shared 函数` 和 `两个 public shared 函数`,而类本身必须声明为 `MustInherit`。现在我将解释原因。
当您有一个类时,您通常需要实例化一个对象才能访问其函数。但是当您将它们声明为“`shared`”时,您就不需要了。并且该类被声明为“`MustInherit`”意味着不能从此类创建对象,您必须将其用作基类。
以下是一些如何使用函数的示例
- 将内容保存到用户桌面上的名为“config.smp”的文件中
ListViewContent.Save(ListView1, System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "config.smp"))
- 追加当前保存的文件并在 ListView 中添加一个不可见列
ListViewContent.Load(ListView1, System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "config.smp"), True, True, True)
- 追加当前保存的文件而不加载 ColumnHeaders
ListViewContent.Load(ListView1, System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "config.smp"), True, False)
示例应用
我还编写了一个演示应用程序来展示如何使用我的 ListViewContent 类。
在此程序中,您可以向 ListView 控件添加组和项目。如果您在 ComboBox 中选择“-none-”项,那么 ListView 中的组将被 `隐藏`(将其属性“`ShowGroups`”设置为“`False`”)。
使用添加不可见 HeaderColumn 的选项加载后,您可以选中 CheckBox 以居中文本的所有 HeaderColumns。
历史
- 2012 年 2 月 23 日 – 发布第一个版本。