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