Accessing Content Through Word's Range Object

Word's programming model centers around the Range object, which represents a contiguous area of text. The Range object allows you to retrieve or set text, and apply text formatting in any position in a document. On this object, you'll find the methods and properties that allow you to manipulate or create text. As you'll learn in the following examples, when you insert content other than text, you need to specify where the content, such as a shape or embedded object, is anchored.

A range of text has a starting and ending character position and exists in distinct parts of the document, such as the main body, headers, footers, endnotes, and comments. Thus, there are many ways to access the Range object, including access from a cell in a table, a header, or a paragraph object. The following line of code provides the most common access to the Range object:

ActiveDocument.Range.Text

If you type this line after a question mark ('?') in the Immediate window in the Visual Basic Editor in Word, the program will print the main text of the document in the Immediate window.

TIP
The code listed here that describes how to insert and manipulate document content in Word is also found in the WdContnt.bas code module in the Chapter 6 practice folder on the CD that comes with this book.

Handling Text

The Range object allows you access to all text, no matter where it is in a document. You can set the Range object so that it represents all or only a portion of a range of text. If you set a variable to the line ActiveDocument.Range, the variable represents the range of text in the main body in a document. This could be anywhere from a single paragraph to many pages of text. Within every Range object, you can return more granular units of text. The Range object allows you to iterate through units of text from as large as a section to as small as a paragraph, sentence, word, and even a single character.

Determining Where Text Belongs

Whenever you return a Range object, you can use the Paragraphs, Sentences, Words, or Character properties to return the collection of paragraphs, sentences, words, or characters from a range of text. One of the nice things about the Range object in Word is that from any Range object you can navigate up and down the text "hierarchy" tree. What this means is that if you assign a variable representing a word to a Range object, you can determine what sentence, paragraph, or section the word is in. The same holds for any unit of text.

Sub NavigateUpTextHierarchy()
    Dim rng As Range
    If ActiveWindow.Selection.Type = wdSelectionIP Then
        Set rng = ActiveWindow.Selection.Range
        Debug.Print rng.Words(1)
        Debug.Print rng.Sentences(1)
        Debug.Print rng.Paragraphs(1).Range
    End If
End Sub

Even if a Range object represents an insertion point (as is commonly the case), when you return the active selection in a window, you can determine what word the insertion point is in. (An insertion point occurs when the blinking cursor is between two characters of text.) See Chapter 9 for more information. In the procedure above, the If…Then statement determines if the selection is an insertion point. If it is, you set the rng variable to the range that represents the insertion. Although there's no text in the rng, the last three lines in the If…Then block print to the Immediate window in the Visual Basic Editor in Word the text of the word, sentence, and paragraph where the insertion point resides.

Iterate Through Words

The following are two different procedures that each search for the word the and keep track of how many instances are found. The first procedure can be very slow, depending on how many words the document has. The For Each…Next loop iterates through the Words collection in the main body text of the document. The If…Then statement uses the built-in Visual Basic for Applications functions Trim$ and LCase$ to remove the spaces after the text of the word and sets the text of the word to lowercase. The number of occurrences found is printed to the Immediate window.

NOTE
In Word, a unit of text representing a word consists of the text of the word followed by the space between the text and the next word. If you double-click on a word in a Word document, the text and the space after the text is highlighted. The same principle holds for sentences. A sentence in Word consists of the text in the sentence followed by the space after the period. A paragraph includes the text, the space after the end of the text (if any), and the paragraph marker.

Sub IterateThroughWords()
    Dim wrd As Range, iCount As Integer
    For Each wrd In ActiveDocument.Range.Words
        If Trim$(LCase$(wrd.Text)) = "the" Then
            iCount = iCount + 1
        End If
    Next wrd
    Debug.Print "The word 'the' used " & iCount & " times."
End Sub

You can also use Word's Find object as a method of finding the number of instances a word is found in a document. As you'll see later in this section, the functionality in the Find object represents the functionality in the Find dialog box in Word. Your code uses the Find object to take advantage of Word's built-in search compatibilities rather than relying on the previous procedure's word iteration.

Sub IterateThroughWordsUsingFindMethod()
    Dim iCount As Integer
    With ActiveDocument.Range.Find
        .ClearFormatting
        .MatchWholeWord = True
        
        Do While .Execute(FindText:= "the", _
                 Forward:=True, Format:=True) = True
            With .Parent ' returns Range object
                iCount = iCount + 1
                .Move Unit:=wdWord, Count:=1
            End With
        Loop
    End With
    
    Debug.Print "The word 'the' used " & _
        iCount & " times."
End Sub

The use of the Find object is described under the heading "Find and Replace."

Insert Paragraphs

When you build document content programmatically, you commonly have to insert paragraphs. Word provides two convenient methods for creating a new paragraph before or after an existing one: InsertParagraphBefore and InsertParagraphAfter. When you access a range representing an existing paragraph or the beginning or end of a paragraph, you can use either method to insert a new paragraph.

You can also use the InsertBefore and InsertAfter methods to insert text. The following procedure assumes that the active document contains at least three paragraphs. The code inserts two new paragraphs before the third one, inserts text into the second new paragraph added, and then adds another paragraph after the second. This gives the appearance of adding a blank line, a new paragraph, and another blank line between the initial second and third paragraphs.

Sub InsertNewParagraphWithLineSpaces()
    Dim rng As Range
    Set rng = ActiveDocument.Paragraphs(3).Range
    With rng
        .InsertParagraphBefore
        .InsertParagraphBefore
        .InsertBefore "New paragraph."
        .InsertParagraphBefore
    End With
End Sub

Of course, you can reduce the code above to just insert a new paragraph between the second and third paragraphs without the two blank lines by removing the two extra lines containing the InsertParagraphBefore method.

Sub InsertNewParagraph()
    Dim rng As Range
    Set rng = ActiveDocument.Paragraphs(3).Range
    With rng
        .InsertParagraphBefore
        .InsertBefore "New paragraph."
    End With
End Sub

Another very common task in solutions is to insert content at the end of text in a document's main body. The following procedure inserts text at the end of a range using the combination of the InsertParagraphAfter and InsertAfter methods:

Sub InsertNewParagraphWithInsertAfter()
    Dim rngEndOfDoc As Range
    With ActiveDocument.Paragraphs.Last.Range
        .InsertParagraphAfter
        .InsertAfter "New paragraph."
    End With
End Sub

The section "Inserting Content at the End of a Document" below describes in more depth how to add content in this location.

Iterate Through Paragraphs in a Bulleted or Numbered List

Word provides a way of determining if a paragraph exists within a list. Two main list types exist: numbered and bulleted. To detect if a paragraph, sentence, word, or character exists in a list, you can use the ListType property on the ListFormat object. You can access the ListFormat object from a Range object.

Sub IterateThroughParagraphsInBulletedLists()
    Dim para As Paragraph
    For Each para In ActiveDocument.Paragraphs
        If para.Range.ListFormat _
            .ListType = wdListBullet Then
            Debug.Print para.Range.Text
        End If
    Next para
End Sub

This procedure iterates through the paragraphs in the main body of the active document. If the list type is bulleted, the text of the bulleted paragraph is printed to the Immediate window in the Visual Basic Editor.

Inserting Content at the End of a Document

You'll often find Office Visual Basic for Applications solutions that use Word to create a report based on, for example, data in a database. You write the code so that it inserts text and data progressively into the document. The main heading is inserted, followed by text, then data, and so on. At each point, the code finds the end of the document and inserts new content. Other scenarios that involve inserting content at the end of the document include adding a new section or an appendix to an existing document. Use the following With…End block to return a Range object that represents the end of a document. The Range object returned consists only of the last character in the main text.

Dim rngEndOfDoc As Range
Set rngEndOfDoc = ActiveDocument.Range
With rngEndOfDoc
    .Collapse Direction:=wdCollapseEnd
    ' use methods here to insert text into document
End With

Once you've returned the last character in the main body of text in a document, you can use a few methods to insert content at the end of the document.

Insert a Paragraph at the End of the Main Body

The Collapse method collapses a range to the starting or ending position, depending on what direction you specify in the Direction argument of the Collapse method. The starting and ending points become equal when a range is collapsed.

Sub InsertNewParagraphUsingCollapse()
    Dim rng As Range
    Set rng = ActiveDocument.Range
    With rng
        .Collapse Direction:=wdCollapseEnd
        .InsertParagraphAfter
        .InsertAfter "End of document."
    End With
End Sub

As mentioned in the Quick Guide section at the beginning of this chapter, in code there is more than one way to accomplish the same task. The main difference between this procedure and the following one is how the range at the end of the document is returned. In the following procedure, the Collapse procedure isn't required when the range of the last paragraph is accessed:

Sub InsertNewParagraphUsingInsertAfter()
    With ActiveDocument.Paragraphs.Last.Range
        .InsertParagraphAfter
        .InsertAfter "End of document."
    End With
End Sub

Insert a Table at the End of a Document

When you add a table to a document, you need to specify the range where the table is to appear. If the range is more than an insertion point, the table replaces the range when inserted. In the following procedure, you insert the table at the end of the document. In the With...End block, a paragraph of text that represents a heading for the table is added before the table.

The InsertParagraphAfter method inserts the new paragraph at the end of the document. The range is then collapsed to a single character position at the end of the document. The text "Data: " is added and formatted as bold. A second paragraph is inserted, the range collapsed again and the new table with five rows and three columns is inserted.

Sub InsertTableAtEndOfDocument()
    Dim rngEndOfDoc As Range
    Set rngEndOfDoc = ActiveDocument.Range
    With rngEndOfDoc
        .InsertParagraphAfter
        .Collapse Direction:=wdCollapseEnd
        .Text = "Data: "
        .Font.Bold = True
        
        .InsertParagraphAfter
        .Collapse Direction:=wdCollapseEnd
        .Tables.Add rngEndOfDoc, 5, 3
    End With
End Sub

Alternately, you can set rngEndOfDoc to the range of the last paragraph. To do this, change the object expression of the Set statement in the procedure above to ActiveDocument.Paragraphs.Last.Range. For another example of this, see the code in the previous section.

Insert a Table Between Paragraphs

You use similar code to insert a table between paragraphs as you do to add it to the end of the document's main body. The main difference is that instead of specifying the range of the document and then collapsing the range to a single character position, you return the range of the paragraph where you want to insert the table.

Sub InsertTableBetweenParagraphs()
    Dim rng As Range
    Set rng = ActiveDocument.Paragraphs(1).Range
    With rng
        .Collapse Direction:=wdCollapseEnd
        .Tables.Add rng, 5, 3
    End With
End Sub

Text Formatting

You can access the methods and properties that allow your code to format text through the Range object to such objects as Font and ParagraphFormat. The Font object, which is found in Word, Excel, and PowerPoint, is used to set text attributes such as bold, italic, and color. The ParagraphFormat object allows your code to set properties such as indent level, borders, and shading. The ParagraphFormat object contains access to objects, methods, and properties that represent functionality in the Paragraph and the Borders And Shading dialog boxes. You can access both of these dialog boxes from the Format menu in Word.

Sub FormatText()
    With ActiveDocument.Paragraphs(1).Range

        With .Font
            .Bold = True
            .Italic = True
            .Color = wdColorBlueGray
        End With

        With .ParagraphFormat
            .FirstLineIndent = InchesToPoints(1)
            .Alignment = wdAlignParagraphCenter
            With .Borders(wdBorderTop)
                .LineStyle = wdLineStyleSingle
                .LineWidth = wdLineWidth050pt
            End With
            With .Borders(wdBorderBottom)
                .LineStyle = wdLineStyleSingle
                .LineWidth = wdLineWidth225pt
            End With
        End With

    End With
End Sub

This procedure applies formatting to the first paragraph of the active document and sets font attributes of the first paragraph, along with setting the indent of the first line of the paragraph to 1 inch. It centers the paragraph alignment and applies top and bottom borders of different widths.

Stories

A Word document can contain text in parts that are distinct from one another. For example, you insert headers and footers that are independent of the text in the main body. Word calls each distinct area of text a story and represents the distinction in the StoryRanges collection. This collection allows you to access the 11 different types of stories that can be part of a document. A document's main body, which has most of your text, is referred to as the main text story. If a document contains not only main body text, but also headers, footers, and comments, there are four stories: main text, header, footer, and comment. The enumeration WdStoryType contains the 11 different story constants. Some examples of constants are the values wdMainTextStory, wdFootnotesStory, wdEndnotesStory, and wdCommentsStory.

Since you most commonly access the main text story, Word provides two ways to access it. The Range property and the Content property on the Document object each returns a Range object representing the document's main text. In Word the lines on the next page all return a Range object representing the same text in the document.

ActiveDocument.Content
ActiveDocument.Range
ActiveDocument.StoryRanges(wdMainTextStory)

If you typed a question mark ('?') followed by any of the lines above in the Immediate window in Word's Visual Basic Editor, the same text content would be printed. Since the Text property is the default property of the Range object, Visual Basic returns the text of the range specified in the line of code.

Text Styles

The Style drop-down list on Word's Formatting toolbar lists a document's built-in and user-defined paragraph and character styles. The most common styles are the predefined set in the Normal.dot template: Normal, Heading 1, Heading 2, and Heading 3. Depending on how you use and define the template and styles, the list can vary considerably. Word has a number of built-in styles that are listed as constants in the WdBuiltInStyle enumeration. The name of each built-in style depends on the language version of Word you installed on a machine. For example, the name for the built-in style "Normal" changes to its Japanese equivalent on a Japanese installation of Word. The constants let you determine if a paragraph has a specific built-in style without having to worry about what the local name of the style is. Word automatically determines the local name of a style.

Sub IterateThroughParagraphsByStyle()
    Dim para As Paragraph
    For Each para In ActiveDocument.Paragraphs
        If para.Style = _
            ActiveDocument.Styles(wdStyleNormal) Then
            Debug.Print para.Range.Text
        End If
    Next para
End Sub

This procedure uses the constant wdStyleNormal to determine if the name of the style applied to a paragraph, represented by the object variable para, is the same as the built-in Normal style. If so, the text is printed to the Immediate window in the Visual Basic Editor.

Create an Outline Document Based on Another Document's Paragraph Styles

You can use the technique just described to do things like copy paragraphs that have particular styles. In the following procedure, you create a new document based on the text of the active document. You use a For Each…Next loop to iterate through the paragraphs of the active document, and the Select Case statement determines if the paragraph style is Heading 1, Heading 2, or Heading 3. If the paragraph style is any of these styles, the procedure copies the formatted text to the end of the text range of the new document created. The new document represents the outline of the active document.

Sub CreateOutlineDocument()
    Dim para As Paragraph
    Dim docActive As Document, docOutline As Document
    
    Set docActive = ActiveDocument
    Set docOutline = Application.Documents.Add
    
    For Each para In docActive.Paragraphs
        Select Case para.Style
        
        Case docActive.Styles(wdStyleHeading1), _
            docActive.Styles(wdStyleHeading2), _
            docActive.Styles(wdStyleHeading3)
            
            With docOutline.Range
                .Collapse wdCollapseEnd
                .FormattedText = _
                    para.Range.FormattedText
                .InsertParagraphAfter
            End With
        End Select
    Next para
End Sub

One of the interesting things in this procedure is the line:

.FormattedText = para.Range.FormattedText

The FormattedText property returns a range containing not just the text but the character formatting from the specified range as well. The line of code above retrieves the formatted text in the active document and applies the formatted text in the new outline document. If you don't want to apply the same text formatting to the outline document, replace the line above with the following one, which sets just the text in the new outline document.

.Text = para.Range.Text

PowerPoint and Excel don't have an equivalent to the FormattedText property. Excel does, however, provide a PasteSpecial method to specify inserting rich text formatting for the cell data and text. This allows you to copy and paste cell data and text, along with cell and text formatting, from one cell to another in any workbook. PowerPoint doesn't have an equivalent to the PasteSpecial method.

Find and Replace

Word has the best text-finding capabilities in Office. You'll find this quality reflected in the Find dialog box in Word, which you access by clicking Find on the Edit menu. In Excel and PowerPoint, the Find dialog box doesn't provide as many choices for narrowing down a text search. The following procedures were written so that the procedure that actually finds and replaces the text is separate from the code that specifies what text needs to be found and what text should replace it. This allows you to call the FindAndReplace procedure from many places in your project.

Sub TestFindAndReplace()
    FindAndReplace sFindText:="dog", _
        sReplaceWithText:="CAT"
End Sub

Sub FindAndReplace(sFindText As String, _
    sReplaceWithText As String)

    With ActiveDocument.Range.Find
        .ClearFormatting
        .MatchWholeWord = True
        
        Do While .Execute(FindText:=sFindText, _
            Forward:=True, Format:=True) = True

            With .Parent ' returns Range object
                .Text = sReplaceWithText
                .Bold = True
                .Move Unit:=wdWord, Count:=1
            End With
        Loop
    End With
End Sub

For example purposes, note that the text that's found and replaced is bolded. Although it's added here to provide a visual indicator in the active document where the text has been replaced, you should remove this line. This example also assumes that you have the word "dog" in your document. You can replace the find and replace text in TestFindAndReplace with any text.

Find and Replace Hyperlinks

Word 2000, like Excel and PowerPoint, makes it easy for you to create HTML documents that you can display in your Web browser with the same fidelity as in Word. You just save your documents with the HTML file format in the Save As dialog box or with the SaveAs method (as discussed in Chapter 4). HTML documents in your Web browser commonly contain hyperlinks. Office's new Insert Hyperlink dialog box, which you display by clicking Hyperlink on the Insert menu, provides a simple way to add hyperlinks to document content.

Sub TestFindAndReplaceHyperlinks()
    FindAndReplaceHyperlinks sFindText:="Microsoft", _
        sHyperlink:="http://www.microsoft.com"
End Sub

Sub FindAndReplaceHyperlinks( _
    sFindText As String, sHyperlink As String)
    Dim rng As Range
    
    With ActiveDocument.Content.Find
        .ClearFormatting
        
        Do While .Execute(FindText:=sFindText, _
            Forward:=True, Format:=True) = True
            
            Set rng = .Parent
            With rng
                .Hyperlinks.Add rng, sHyperlink
                .Font.Color = wdColorSeaGreen
                .Move Unit:=wdWord, Count:=1
            End With
        Loop
    End With
End Sub

In the active document add some text with the word "Microsoft" scattered throughout. Run the TestFindAndReplaceHyperlinks subroutine from a code module in the Visual Basic Editor in Word. This procedure passes the text to be searched for in the active document. It also passes the hyperlink URL that should be added to the text when it's found. When the Do…Loop in the second procedure finds an instance of the search text "Microsoft," the program inserts the hyperlink http://www.microsoft.com and sets the color of the text "Microsoft" to sea green. To preview the HTML document in the Web browser, click Web Page Preview from the File menu in Word. Click on the word "Microsoft" and the Web browser should navigate to Microsoft's home page (if your computer is connected to the Internet).

Tables

Tables are common content elements found in many Office documents. The following two procedures create a simple five-row by three-column table at the end of the active document's main body. The new table is set to the variable tbl, and the second With…End block in the procedure sets properties of the table. Within the With…End block, you format the table to the Classic 1 format (listed in the Table AutoFormat dialog box, Table menu). The program then calls the procedure InsertTableData, which fills the second and third columns with random values.

Sub InsertTable()
    Dim tbl As Table, rngEndOfDoc As Range
    Set rngEndOfDoc = ActiveDocument.Range
    With rngEndOfDoc
        .Collapse Direction:=wdCollapseEnd
        Set tbl = .Tables.Add(rngEndOfDoc, 5, 3)
    End With
    
    With tbl
        .AutoFormat Format:=wdTableFormatClassic1
        InsertTableData tbl
        .Columns.AutoFit
    End With
End Sub

Sub InsertTableData(tbl As Table)
    Dim x As Integer, y As Integer
    With tbl
        For x = 2 To .Rows.Count
            For y = 2 To .Columns.Count
                .Cell(x, y).Range.Text = _
                    Format$(Rnd(x + y), "###0.00")
            Next y
        Next x
    End With
End Sub

Because the data's source can vary, you write the code to insert data into the table so that it exists in a separate procedure. This code separation keeps table insertion and formatting separate from the data that's inserted.

Shapes: Inline and Floating

You can insert shapes into a Word document as either floating shapes or inline shapes. Word is the only application that has two distinct shape collections. Excel and PowerPoint have only the Shapes collection, but Word has both the InlineShapes and the Shapes collections. The difference, as the name InlineShapes implies, is that some shapes will exist between two text characters (that is, in line with the text). The inline shape resides within a character position in a range's text stream. Objects in the Shapes collection, on the other hand, float on a content layer above or below the text in the Word document.

Although text can be wrapped around an object so that the object appears to be inline with the text, technically the object lives above the text. AutoShapes can only be free-floating, but pictures, for example, can be inserted as either inline or free-floating. You can change the layout of a picture shape by using the ConvertToInlineShape method and the ConvertToShape method to convert shapes from one type to the other. Also, you can choose from any of the wrapping styles to convert the shape from one type to the other by selecting a picture, clicking Format Picture on the Format menu, and displaying the Layout tab.