Working with Word for WPF > Advanced Level Working > Adding TOC |
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: