Sorting TOCs in a Project

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

Leave a comment

Your email address will not be published.

HTML tags are not allowed.

253,264 Spambots Blocked by Simple Comments