ComponentOne Word for WPF
Working with Word for WPF / Advanced Level Working / Adding TOC
In This Topic
    Adding TOC
    In This Topic

    Word for WPF enables you to add TOC to a word document containing text with the respective headers. These headers can then be used to create a TOC.

    Note that a class named WordUtils is used in the code given below. It is available in the product sample located at the following location on your system:
    Documents\ComponentOne Samples\WPF\WordCreator
    You can use this class in your application from the mentioned location.

    Add the following lines of code inside the deriving Window class, to generate a random number and use a string reference:

    Shared rnd As New Random()
    Shared _extension As String = ".rtf"
                            
    
    static Random rnd = new Random();
    static string _extension = ".rtf";
    

    The following code creates content and a TOC for a word document with text and headers in it:

    ' create document
    Dim word = New C1WordDocument()
    word.Clear()
    word.Info.Title = "Document with Table of Contents"
    
    ' add title
    Dim titleFont As New Font("Tahoma", 24, RtfFontStyle.Bold)
    Dim rcPage As Rect = WordUtils.PageRectangle(word)
    Dim rc As Rect = WordUtils.RenderParagraph(word, word.Info.Title, titleFont, rcPage, rcPage, False)
    rc.Y += 12
    
    ' create nonsense document
    Dim bkmk = New List()
    Dim headerFont As New Font("Arial", 14, RtfFontStyle.Bold)
    Dim bodyFont As New Font("Times New Roman", 11)
    For i As Integer = 0 To 29
            ' create ith header (as a link target and outline entry)
            Dim header As String = String.Format("{0}. {1}", i + 1, BuildRandomTitle())
            rc = WordUtils.RenderParagraph(word, header, headerFont, rcPage, rc, True, _
                    True)
    
            ' save bookmark to build TOC later
            Dim pageNumber As Integer = 1
            bkmk.Add(New String() {pageNumber.ToString(), header})
    
            ' create some text
            rc.X += 36
            rc.Width -= 36
    
            For j As Integer = 0 To 3 + (rnd.[Next](20) - 1)
                    Dim text As String = BuildRandomParagraph()
                    rc = WordUtils.RenderParagraph(word, text, bodyFont, rcPage, rc)
                    rc.Y += 6
            Next
            rc.X -= 36
            rc.Width += 36
            rc.Y += 20
    Next
    
    ' start Table of Contents
    word.PageBreak()
    ' start TOC on a new page
    rc = WordUtils.RenderParagraph(word, "Table of Contents", titleFont, rcPage, rcPage, True)
    rc.Y += 12
    rc.X += 30
    rc.Width -= 40
    
    ' render Table of Contents
    Dim dottedPen As New C1.WPF.Word.Pen(Colors.Gray, 1.5F)
    dottedPen.DashStyle = C1.WPF.Word.DashStyle.Dot
    Dim sfRight As New StringFormat()
    sfRight.Alignment = HorizontalAlignment.Right
    rc.Height = bodyFont.Size * 1.2
    
    For Each entry As String() In bkmk
            ' get bookmark info
            Dim page As String = entry(0)
            Dim header As String = entry(1)
    
            ' render header name and page number
            word.DrawString(header, bodyFont, Colors.Black, rc)
            word.DrawString(page, bodyFont, Colors.Black, rc, sfRight)
    
    
            ' connect the two with some dots (looks better than a dotted line)
            Dim dots As String = ". "
            Dim wid = word.MeasureString(dots, bodyFont).Width
            Dim x1 = rc.X + word.MeasureString(header, bodyFont).Width + 8
            Dim x2 = rc.Right - word.MeasureString(page, bodyFont).Width - 8
            Dim x = rc.X
            rc.X = x1
            While rc.X < x2
                    word.DrawString(dots, bodyFont, Colors.Gray, rc)
                    rc.X += wid
            End While
            rc.X = x
    
            ' move on to next entry
            rc = WordUtils.Offset(rc, 0, rc.Height)
            If rc.Bottom > rcPage.Bottom Then
                    word.PageBreak()
                    rc.Y = rcPage.Y
            End If
    Next
    
    ' get stream to save to
    Dim dlg = New SaveFileDialog()
    dlg.FileName = "Sample document"
    dlg.DefaultExt = ".docx"
    dlg.Filter = WordUtils.GetFileFilter(_extension)
    Dim dr = dlg.ShowDialog()
    If Not dr.HasValue OrElse Not dr.Value Then
            Return
    End If
    
    ' save document
    Using stream = dlg.OpenFile()
            word.Save(stream, If(dlg.FileName.ToLower().EndsWith(".docx"), FileFormat.OpenXml, FileFormat.Rtf))
    End Using
    MessageBox.Show("Word Document saved to " + dlg.SafeFileName)
    
    // create document
    var word = new C1WordDocument();
    word.Clear();
    word.Info.Title = "Document with Table of Contents";
    
    // add title
    Font titleFont = new Font("Tahoma", 24, RtfFontStyle.Bold);
    Rect rcPage = WordUtils.PageRectangle(word);
    Rect rc = WordUtils.RenderParagraph(word, word.Info.Title, titleFont, rcPage, rcPage, false);
    rc.Y += 12;
    
    // create nonsense document
    var bkmk = new List();
    Font headerFont = new Font("Arial", 14, RtfFontStyle.Bold);
    Font bodyFont = new Font("Times New Roman", 11);
    for (int i = 0; i < 30; i++)
    {
        // create ith header (as a link target and outline entry)
        string header = string.Format("{0}. {1}", i + 1, BuildRandomTitle());
        rc = WordUtils.RenderParagraph(word, header, headerFont, rcPage, rc, true, true);
    
         // save bookmark to build TOC later
         int pageNumber = 1;
         bkmk.Add(new string[] { pageNumber.ToString(), header });
    
         // create some text
         rc.X += 36;
         rc.Width -= 36;
    
         for (int j = 0; j < 3 + rnd.Next(20); j++)
         {
              string text = BuildRandomParagraph();
              rc = WordUtils.RenderParagraph(word, text, bodyFont, rcPage, rc);
              rc.Y += 6;
          }
          rc.X -= 36;
          rc.Width += 36;
          rc.Y += 20;
    }
    
    // start Table of Contents
    word.PageBreak();                   // start TOC on a new page
    rc = WordUtils.RenderParagraph(word, "Table of Contents", titleFont, rcPage, rcPage, true);
    rc.Y += 12;
    rc.X += 30;
    rc.Width -= 40;
    
    // render Table of Contents
    C1.WPF.Word.Pen dottedPen = new C1.WPF.Word.Pen(Colors.Gray, 1.5f);
    dottedPen.DashStyle = C1.WPF.Word.DashStyle.Dot;
    StringFormat sfRight = new StringFormat();
    sfRight.Alignment = HorizontalAlignment.Right;
    rc.Height = bodyFont.Size * 1.2;
    
    foreach (string[] entry in bkmk)
    {
         // get bookmark info
         string page = entry[0];
         string header = entry[1];
    
          // render header name and page number
          word.DrawString(header, bodyFont, Colors.Black, rc);
          word.DrawString(page, bodyFont, Colors.Black, rc, sfRight);
    
    
          // connect the two with some dots (looks better than a dotted line)
          string dots = ". ";
          var wid = word.MeasureString(dots, bodyFont).Width;
          var x1 = rc.X + word.MeasureString(header, bodyFont).Width + 8;
          var x2 = rc.Right - word.MeasureString(page, bodyFont).Width - 8;
          var x = rc.X;
          for (rc.X = x1; rc.X < x2; rc.X += wid)
          {
               word.DrawString(dots, bodyFont, Colors.Gray, rc);
          }
          rc.X = x;
    
          // move on to next entry
          rc = WordUtils.Offset(rc, 0, rc.Height);
          if (rc.Bottom > rcPage.Bottom)
          {
               word.PageBreak();
               rc.Y = rcPage.Y;
          }
    }
                
    // get stream to save to
    var dlg = new SaveFileDialog();
    dlg.FileName = "Sample document";
    dlg.DefaultExt = ".docx";
    dlg.Filter = WordUtils.GetFileFilter(_extension);
    var dr = dlg.ShowDialog();
    if (!dr.HasValue || !dr.Value)
    {
         return;
    }
    
    // save document
    using (var stream = dlg.OpenFile())
    {
         word.Save(stream, dlg.FileName.ToLower().EndsWith(".docx") ? FileFormat.OpenXml : FileFormat.Rtf);
    }
    MessageBox.Show("Word Document saved to " + dlg.SafeFileName);
    

    The above code adds bookmark to the headers of the text in document. These bookmarks are then used to generate a TOC.

    As you can see in the above code, we have used two methods, BuildRandomParagraph and BuildRandomTitle. These methods contain array of strings and return these strings in specified format as shown in the following code. Similarly, you can create your own methods and string values.

    Private Shared Function BuildRandomTitle() As String
        Dim a1 As String() = "Learning|Explaining|Mastering|Forgetting|Examining|Understanding|Applying|Using|Destroying".Split("|"C)
        Dim a2 As String() = "Music|Tennis|Golf|Zen|Diving|Modern Art|Gardening|Architecture|Mathematics|Investments|.NET|Java".Split("|"C)
        Dim a3 As String() = "Quickly|Painlessly|The Hard Way|Slowly|Painfully|With Panache".Split("|"C)
        Return String.Format("{0} {1} {2}", a1(rnd.[Next](a1.Length - 1)), a2(rnd.[Next](a2.Length - 1)), a3(rnd.[Next](a3.Length - 1)))
    End Function
    
    Private Shared Function BuildRandomParagraph() As String
        Dim sb As New StringBuilder()
        For i As Integer = 0 To 5 + (rnd.[Next](10) - 1)
              sb.AppendFormat(BuildRandomSentence())
        Next
        Return sb.ToString()
    End Function
    
    Private Shared Function BuildRandomSentence() As String
        Dim a1 As String() = "Artists|Movie stars|Musicians|Politicians|Computer programmers|Modern thinkers|Gardeners|Experts|Some people|Hockey players".Split("|"C)
        Dim a2 As String() = "know|seem to think about|care about|often discuss|dream about|hate|love|despise|respect|long for|pay attention to|embrace".Split("|"C)
        Dim a3 As String() = "the movies|chicken soup|tea|many things|sushi|my car|deep thoughs|tasteless jokes|vaporware|cell phones|hot dogs|ballgames".Split("|"C)
        Dim a4 As String() = "incessantly|too much|easily|without reason|rapidly|sadly|randomly|vigorously|more than usual|with enthusiasm|shamelessly|on Tuesdays".Split("|"C)
        Return String.Format("{0} {1} {2} {3}. ", a1(rnd.[Next](a1.Length - 1)), a2(rnd.[Next](a2.Length - 1)), a3(rnd.[Next](a3.Length - 1)), a4(rnd.[Next](a4.Length - 1)))
    End Function
    
    static string BuildRandomTitle() {
      string[] a1 = "Learning|Explaining|Mastering|Forgetting|Examining|Understanding|Applying|Using|Destroying".Split('|');
      string[] a2 = "Music|Tennis|Golf|Zen|Diving|Modern Art|Gardening|Architecture|Mathematics|Investments|.NET|Java".Split('|');
      string[] a3 = "Quickly|Painlessly|The Hard Way|Slowly|Painfully|With Panache".Split('|');
      return string.Format("{0} {1} {2}", a1[rnd.Next(a1.Length - 1)], a2[rnd.Next(a2.Length - 1)], a3[rnd.Next(a3.Length - 1)]);
    }
    
    static string BuildRandomParagraph() {
      StringBuilder sb = new StringBuilder();
      for (int i = 0; i < 5 + rnd.Next(10); i++) {
        sb.AppendFormat(BuildRandomSentence());
      }
      return sb.ToString();
    }
    
    static string BuildRandomSentence() {
      string[] a1 = "Artists|Movie stars|Musicians|Politicians|Computer programmers|Modern thinkers|Gardeners|Experts|Some people|Hockey players".Split('|');
      string[] a2 = "know|seem to think about|care about|often discuss|dream about|hate|love|despise|respect|long for|pay attention to|embrace".Split('|');
      string[] a3 = "the movies|chicken soup|tea|many things|sushi|my car|deep thoughs|tasteless jokes|vaporware|cell phones|hot dogs|ballgames".Split('|');
      string[] a4 = "incessantly|too much|easily|without reason|rapidly|sadly|randomly|vigorously|more than usual|with enthusiasm|shamelessly|on Tuesdays".Split('|');
      return string.Format("{0} {1} {2} {3}. ", a1[rnd.Next(a1.Length - 1)], a2[rnd.Next(a2.Length - 1)], a3[rnd.Next(a3.Length - 1)], a4[rnd.Next(a4.Length - 1)]);
    }
    

    The output will look similar to the image given below: