TOC sorting is probably only useful for reference documentation. But please post comments with any use-cases for TOC sorting you can think of. My desire to sort a TOC is threefold. I want to be able to:
- Flatten a TOC with levels down to one level
- Sort a flattened TOC in ascending order by the Title attribute of a TocEntry
- Sort a TOC with a multi-level hierarchy in ascending order by the Title attribute of a TocEntry and respect the level structure
The UI is left to you.
Create a Visual Basic Windows Forms project and build a form with items which correspond to the procedures in the sample. An open file dialog is necessary to select the TOC. With the dialogs, limit the file type to *.fltoc. The TOC structure is rendered in a tree view. I placed it prominently in the center of the form. I used buttons for the options. But radio buttons would make more sense.
A preview of the options display in the tree view when a button is clicked. But nothing is saved until the save dialog is shown. You can use that to overwrite or to create a new TOC. If I see any use-cases, I’ll post a full sample project in a later post.
Imports System.Xml
Public Class Form1
Private NewToc As XDocument
Private Sub ButtonOpen_Click(sender As System.Object, e As System.EventArgs) Handles ButtonOpen.Click
OpenFileDialogToc.ShowDialog()
End Sub
Private Sub ButtonSave_Click(sender As System.Object, e As System.EventArgs) Handles ButtonSave.Click
If String.IsNullOrWhiteSpace(OpenFileDialogToc.FileName) Or Not My.Computer.FileSystem.FileExists(OpenFileDialogToc.FileName) Then
MsgBox("A file has not been selected.")
Else
SaveFileDialogToc.ShowDialog()
End If
End Sub
Private Sub OpenFileDialogToc_FileOk(sender As System.Object, e As System.ComponentModel.CancelEventArgs) Handles OpenFileDialogToc.FileOk
Dim Toc As XmlDocument = New XmlDocument()
Toc.Load(OpenFileDialogToc.FileName)
RefreshTree(Toc)
NewToc = XDocument.Load(OpenFileDialogToc.FileName)
End Sub
Private Sub RefreshTree(ByVal Toc As XmlDocument)
TreeViewToc.Nodes.Clear()
TreeViewToc.Nodes.Add(New TreeNode())
Dim TocNode As TreeNode = New TreeNode()
TocNode = TreeViewToc.Nodes(0)
AddNodeToDisplay(Toc.DocumentElement, TocNode)
TreeViewToc.ExpandAll()
End Sub
Private Sub AddNodeToDisplay(ByVal InTocFileNode As XmlNode, ByVal InTocTreeNode As TreeNode)
Dim TocFileNode As XmlNode
Dim TocTreeNode As TreeNode
Dim TocFileNodeList As XmlNodeList
If InTocFileNode.HasChildNodes Then
TocFileNodeList = InTocFileNode.ChildNodes
For i As Integer = 0 To TocFileNodeList.Count - 1 Step 1
TocFileNode = InTocFileNode.ChildNodes(i)
InTocTreeNode.Nodes.Add(New TreeNode(TocFileNode.Attributes("Title").Value))
TocTreeNode = InTocTreeNode.Nodes(i)
AddNodeToDisplay(TocFileNode, TocTreeNode)
Next
Else
InTocTreeNode.Text = InTocFileNode.Attributes("Title").Value
End If
End Sub
Private Sub Flatten(ByVal Toc As String)
If String.IsNullOrWhiteSpace(OpenFileDialogToc.FileName) Or Not My.Computer.FileSystem.FileExists(OpenFileDialogToc.FileName) Then
MsgBox("A file has not been selected.")
Else
Dim TocXml As XDocument = XDocument.Load(Toc)
Dim FlatToc As XDocument = _
<?xml version="1.0" encoding="utf-8"?>
<CatapultToc
Version="1">
</CatapultToc>
For Each element In TocXml.Root.Descendants
Dim element2 As XElement = _
<TocEntry></TocEntry>
For Each attr In element.Attributes
element2.SetAttributeValue(attr.Name, attr.Value)
Next
FlatToc.Root.Add(element2)
Next
NewToc = FlatToc
LabelChangeApplied.Text = "Flattened"
Dim xd As New XmlDocument
Dim xr = FlatToc.CreateReader()
xd.Load(xr)
RefreshTree(xd)
End If
End Sub
Private Sub SortTopNodes(ByVal Toc As String)
If String.IsNullOrWhiteSpace(OpenFileDialogToc.FileName) Or Not My.Computer.FileSystem.FileExists(OpenFileDialogToc.FileName) Then
MsgBox("A file has not been selected.")
Else
Dim TocXml As XDocument = XDocument.Load(Toc)
Dim SortedToc As XDocument = _
<?xml version="1.0" encoding="utf-8"?>
<CatapultToc
Version="1">
</CatapultToc>
Dim ChildrenOfCatapultToc = From xElement In TocXml.Root.Elements _
Order By CStr(xElement.Attribute("Title")) Ascending _
Select xElement
SortedToc.Root.Add(ChildrenOfCatapultToc)
NewToc = SortedToc
LabelChangeApplied.Text = "Top nodes sorted"
Dim xd As New XmlDocument
Dim xr = SortedToc.CreateReader()
xd.Load(xr)
RefreshTree(xd)
End If
End Sub
Private Sub SortInnerNodes(ByVal Toc As String)
If String.IsNullOrWhiteSpace(OpenFileDialogToc.FileName) Or Not My.Computer.FileSystem.FileExists(OpenFileDialogToc.FileName) Then
MsgBox("A file has not been selected.")
Else
Dim TocXml As XDocument = XDocument.Load(Toc)
Dim SortedToc As XDocument = _
<?xml version="1.0" encoding="utf-8"?>
<CatapultToc
Version="1">
</CatapultToc>
SortTocEntry(TocXml.Root)
SortedToc.Root.ReplaceWith(TocXml.Root)
NewToc = SortedToc
LabelChangeApplied.Text = "All nodes sorted"
Dim xd As New XmlDocument
Dim xr = SortedToc.CreateReader()
xd.Load(xr)
RefreshTree(xd)
End If
End Sub
Private Sub SortTocEntry(ByVal tocEntry As XElement)
If tocEntry.HasElements Then
For Each entry In tocEntry.Elements
SortTocEntry(entry)
Next
tocEntry.ReplaceNodes(From xElement In tocEntry.Elements _
Order By CStr(xElement.Attribute("Title")) Ascending _
Select xElement)
End If
End Sub
Private Sub ButtonFlatten_Click(sender As System.Object, e As System.EventArgs) Handles ButtonFlatten.Click
Flatten(OpenFileDialogToc.FileName)
End Sub
Private Sub SaveFileDialogToc_FileOk(sender As System.Object, e As System.ComponentModel.CancelEventArgs) Handles SaveFileDialogToc.FileOk
NewToc.Save(SaveFileDialogToc.FileName)
End Sub
Private Sub ButtonSortTopNodes_Click(sender As System.Object, e As System.EventArgs) Handles ButtonSortTopNodes.Click
SortTopNodes(OpenFileDialogToc.FileName)
End Sub
Private Sub ButtonSortInnerNodes_Click(sender As System.Object, e As System.EventArgs) Handles ButtonSortInnerNodes.Click
SortInnerNodes(OpenFileDialogToc.FileName)
End Sub
End Class