Working with Word document designs, styles and printing
I know a gentleman that likes to send emails to his “email list”. I put it in quotes because I’m on the list and I didn’t sign up. He knows me, put me on his list, and there I will stay. There is no mechanism for removal.
And… I DON’T CARE!
Why? Because the emails are terrible and I find that entertaining. Actually, I don’t know for certain if they are terrible. I’ve never read one from start to finish. I simply assume they are terrible because they look terrible. They are just a bunch of words with no thought to style and formatting. There is nothing that draws your eye to the content and says “look at me? I’m important! READ ME! READ ME FIRST! Read Me if you read nothing else!”
Treating your readers in this manner IS terrible. It’s not mannerly and it’s disrespectful. Worse, it is a waste of the author’s time and energy.
I like to say to anyone who will listen (and to many others who will not) that, “You have to respect the reader.” Meaning, you need to make the document easy and entertaining to read. It can’t be painful to the eyes. Instead, your document must please the reader’s eye and invite the reader to stay, read, and learn a thing or two.
This is exactly my aim today as I explain how to work with Microsoft Word document design elements as well printing objects.
Designing Microsoft Word documents is not all that different from designing a web page; you want to keep the content and the design separate. In Word, you keep them separate by utilizing styles. Styles include a plethora of design elements (e.g. font, borders, paragraph format, etc). Let’s have some fun with them via code.
I’m taking the first person view with today’s samples. These are things I would find useful in my writing life… if only I would turn them into a viable add-in.
It’s always good to know how to loop through a collection and process all of its objects in one fell swoop. I especially like working with the Styles collection because each style has a slightly different purpose. In this sample, I perform a loop around the Styles collection and change the font to the best font ever.
Private Sub EnumerateStyles() Dim curDoc As Word.Document curDoc = WordApp.ActiveDocument Dim i As Integer 'Change all styles to use my favorite font :-) For i = 1 To curDoc.Styles.Count With curDoc.Styles.Item(i) .Font.Name = "Comic Sans MS" End With Next Marshal.ReleaseComObject(curDoc) End Sub
This should be the default font. Anyone can use Helvetica or Arial. It takes an artist to use this font.
When I create a new Word document or receive one to edit, I find I am always enhancing the heading styles. If the document involves code, I typically add a code style as well. I realize I could add these styles to a template and attach it but that is not how I like to automate. Templates are for users while code is for developers.
In this sample, I add two styles to the document and enhance two others.
Private Sub SetupMyWritingHeadings() Dim curDoc As Word.Document curDoc = WordApp.ActiveDocument Dim styleTitle As Word.Style Dim styleCode As Word.Style 'Add Article Title style styleTitle = curDoc.Styles.Add(Name:="ArticleTitle", _ Type:=Word.WdStyleType.wdStyleTypeParagraph) With styleTitle.Font .Bold = True .Name = "Arial" .Size = 28 .Underline = Word.WdUnderline.wdUnderlineSingle .AllCaps = True End With styleTitle.ParagraphFormat.SpaceAfter = 12 'Add Code Sample style styleCode = curDoc.Styles.Add(Name:="CodeSample", _ Type:=Word.WdStyleType.wdStyleTypeParagraph) With styleCode.Font .Bold = False .Name = "Consolas" .Size = 9 End With With styleCode.ParagraphFormat .SpaceAfter = 6 .SpaceBefore = 6 End With 'Don't include this style in Spell Checks styleCode.NoProofing = True 'Change the default headings to how I like them With curDoc.Styles(Word.WdBuiltinStyle.wdStyleHeading1) .Font.Bold = True .Font.Size = 18 .Font.Name = "Arial" .NameLocal = "Heading1" .ParagraphFormat.SpaceBefore = 12 .ParagraphFormat.SpaceAfter = 6 .ParagraphFormat.PageBreakBefore = True .ParagraphFormat.KeepWithNext = True End With 'Change the default headings to how I like them With curDoc.Styles(Word.WdBuiltinStyle.wdStyleHeading2) .Font.Bold = True .Font.Size = 14 .Font.Name = "Arial" .NameLocal = "Heading2" .ParagraphFormat.SpaceBefore = 9 .ParagraphFormat.SpaceAfter = 3 .ParagraphFormat.KeepWithNext = True End With With curDoc.Styles(Word.WdBuiltinStyle.wdStyleNormal) .Font.Bold = False .Font.Size = 11 .Font.Name = "Arial" .ParagraphFormat.SpaceBefore = 0 .ParagraphFormat.SpaceAfter = 3 .ParagraphFormat.KeepTogether = True End With Marshal.ReleaseComObject(styleTitle) Marshal.ReleaseComObject(styleCode) Marshal.ReleaseComObject(curDoc) End Sub
The code adds two styles: ArticleTitle and CodeSample. For each, the code sets font properties and paragraph format properties. The ParagraphFormat property allows me to control spacing and document flow options like KeepWithNext and PageBreakBefore.
Notice the CodeSample style’s NoProofing property is set to true. This value prevents Microsoft Word from lighting-up with red squiggly, spell-check, lines. Instead, Word ignores this style when spell checking.
Notice too that I rename the styles for Heading 1 and Heading 2. Renaming them using this technique allows me to call them by name when needed. For example, when looking for them as the next sample “samplifies” (My word… I thought about using illustrates but I am not drawing. I am providing samples (VB.NET).
Change to sentence case
Here at the Add-in Express blog, we use Sentence case for headings. I forget about this standard often. This code corrects the issue.
Private Sub ChangeAllHeadingsToSentenceCase(headingLevelName As String) 'Find each heading1 'Change to SentenceCase Dim curDoc As Word.Document = WordApp.ActiveDocument With WordApp.Selection.Find .ClearFormatting() .Forward = True .Text = "" .Style = curDoc.Styles(headingLevelName) .Execute() While .Found WordApp.Selection.Range.Case = Word.WdCharacterCase.wdTitleSentence WordApp.Selection.Collapse(Word.WdCollapseDirection.wdCollapseEnd) .Execute() End While End With Marshal.ReleaseComObject(curDoc) End Sub
I can call the ChangeAllHeadingsToSentenceCase procedure and pass the heading name. This allows for finer control. The code executes Find using the passed heading name. If found, the code loops through the found heading items and changes their case to sentence case. This is guaranteed to make Sveta happy!
Apply a style
When I write an article that includes code samples, I paste the code into Word. I then apply the CodeSample style to it. Sure, I can use the Style Gallery to do it but the following code can be called from anywhere.
Private Sub ApplyCodeSampleStyle() WordApp.Selection.GoTo(What:=Word.WdGoToItem.wdGoToBookmark, Name:="\para") 'This will fail if the style does not exist. WordApp.Selection.Style = WordApp.ActiveDocument.Styles("CodeSample") End Sub
The code utilizes a built-in paragraph bookmark to select the entire paragraph. It then applies CodeSample style. Easy-peasy.
For Word fonts, the common use case is to apply a font to selection of text and then format the font settings. The ListFonts procedure does this and more. Run this puppy and you will have a nice font document that lists each of your installed font name while also providing of a display sample for each.
Private Sub ListFonts() Dim newDoc As Word.Document Dim fontTable As Word.Table 'Create a document to contain the list of Fonts newDoc = WordApp.Documents.Add 'Add a title to the document and apply the Title Style WordApp.Selection.Style = newDoc.Styles("Title") WordApp.Selection.Text = "Font List & Samples" WordApp.Selection.InsertParagraphAfter() Dim i As Integer 'Borrowed and adapted from 'http://support.microsoft.com/kb/209205 'Add a table and set the table header fontTable = newDoc.Tables.Add( _ WordApp.Selection.Range, WordApp.FontNames.Count + 1, 2) With fontTable .Borders.Enable = False .Cell(1, 1).Range.Font.Name = "Arial" .Cell(1, 1).Range.Font.Bold = 1 .Cell(1, 1).Range.InsertAfter("Font Name") .Cell(1, 2).Range.Font.Name = "Arial" .Cell(1, 2).Range.Font.Bold = 1 .Cell(1, 2).Range.InsertAfter("Font Example") End With 'Go through all the fonts and add them to the table For i = 1 To WordApp.FontNames.Count With fontTable .Cell(i + 1, 1).Range.Font.Name = "Arial" .Cell(i + 1, 1).Range.Font.Size = 10 .Cell(i + 1, 1).Range.InsertAfter(WordApp.FontNames(i)) .Cell(i + 1, 2).Range.Font.Name = WordApp.FontNames(i) .Cell(i + 1, 2).Range.Font.Size = 10 .Cell(i + 1, 2).Range.InsertAfter("ABCDEFG abcdefg 1234567890") End With Next i fontTable.Sort(SortOrder:=Word.WdSortOrder.wdSortOrderAscending) Marshal.ReleaseComObject(fontTable) Marshal.ReleaseComObject(newDoc) End Sub
The code creates title and then loops through the FontNames collection to build a table of font samples. Notice the use of the Range object. The Range contains the area where the code inserts new text and the Font formatting occurs.
Word provides a gallery of themes or quick styles that allow users to quickly change the entire look and feel of a Word document. You can control the application of themes as well as save new ones.
Apply a quick style
To apply a theme takes a single line of code:
Private Sub ApplyTheme(themeName As String) WordApp.ActiveDocument.ApplyQuickStyleSet2(themeName) End Sub
This method accepts a theme name as a parameter and then applies it. The code also assumes the theme exists. You might prefer to add error handling in case the theme does not exist.
Note. If you apply a theme and have custom styles in the document, the custom styles will be unaffected by the new theme. Just wanted you to know.
Saving a quick style
You can save a style just as easily as you apply one.
Private Sub SaveDocumentStylingsAsAQuickStyle(styleName) WordApp.ActiveDocument.SaveAsQuickStyleSet(styleName) End Sub
The code above will take all the styles within the current Word document and add a new theme. This theme will be available in the Design tab under Document Formatting.
Formatting and styling are one thing. Printing is another. In some ways, getting the print job executed correctly is 80% of the battle. I know because I have a finicky HP printer that never cooperates. Anyway, the next two code chunks samplify how to take some control of the print process within Microsoft Word.
Configure page setup
For some print jobs, I want to print the first page on nicer piece of paper. This first page is the report cover sheet and resides in the lower bin of my printer. The other pages can print on paper in the default bin. This method takes care of things for me.
Private Sub ConfigThePageSetupForReports(showFieldCodes As Boolean) With WordApp.Selection.PageSetup .Orientation = Word.WdOrientation.wdOrientPortrait 'I like the cover page to print on thicker paper .FirstPageTray = Word.WdPaperTray.wdPrinterLowerBin 'After the first page, pring to the default bin .OtherPagesTray = Word.WdPaperTray.wdPrinterDefaultBin .HeaderDistance = 25 .TopMargin = WordApp.InchesToPoints(3) 'Our letterhead has a really large logo End With WordApp.ActiveWindow.View.ShowFieldCodes = showFieldCodes End Sub
For fun, I set the HeaderDistance to large size to allow for a ridiculous amount of space. I don’t actually have a large logo in my letterhead. But if you do, you now know how to allow room for it. Just note that changes like this will impact your document pagination.
The procedure has Boolean parameter used to hide or show field codes. I sometimes need to see the field codes and you might too.
The DocumentBeforePrint event
The DocumentBeforePrint allows us to control and edit the print job immediately after the user clicks the print button. It’s the ideal place to apply your business logic.
In my scenario, I have three printers.
- An envelope printer for printing envelopes
- A legal printer for printing to legal-sized paper
- A printer contain standard-sized paper
The code takes a look at the document’s PaperSize property and branches.
Private Sub adxWordEvents_DocumentBeforePrint(sender As Object, _ e As ADXHostBeforeActionEventArgs) Handles adxWordEvents.DocumentBeforePrint 'If document's Page size is legal send to the bottom tray Dim docToPrint As Word.Document = WordApp.ActiveDocument Select Case docToPrint.PageSetup.PaperSize Case Word.WdPaperSize.wdPaperEnvelopePersonal WordApp.ActivePrinter = "Envelope Printer" Case Word.WdPaperSize.wdPaperLegal WordApp.ActivePrinter = "Legal Printer" ConfigThePageSetupForReports(False) Case Else WordApp.ActivePrinter = "Standard Printer" End Select Marshal.ReleaseComObject(docToPrint) End Sub
If the paper size is legal, the code changes the ActivePrinter to the legal printer. This is the printer for some fancy reports with fancy report paper, therefore, it calls the ConfigThePageSetupForReports procedure to set things up.
Working with Word document designs and automating the design process is a deep topic. This is an area where you, as a Word solution developer, can dig-in and provide valuable features to your users.
This sample Word add-in was developed using Add-in Express for Office and .net:
Word add-in development in Visual Studio for beginners:
- Part 1: Word add-in development – Application and base objects
- Part 2: Customizing Word UI – What is and isn’t customizable
- Part 3: Customizing Word main menu, context menus and Backstage view
- Part 4: Creating custom Word ribbons and toolbars
- Part 5: Building custom task panes for Word 2013 – 2003
- Part 6: Working with Word document content objects
- Part 8: Working with multiple Microsoft Word documents
- Part 9: Using custom XML parts in Word add-ins
- Part 10: Working with Word document properties, bookmarks, content controls and quick parts
- Part 11: Populating Word documents with data from external sources
- Part 12: Working with Microsoft Word templates