In this chapter:
List Types
The List Object
The ListTemplate Object and ListGalleries
Example: Looking at ListsChapter 17
The List ObjectLists play an important role in Word documents, and the Word object model contains objects that give the programmer very powerful control over lists. Figure 17-1 shows most of these objects, which are children of the Document object. The ListGallery object and ListGalleries collection, which are children of the Application object, are not shown.
Figure 17-1. List-related objects
![]()
The Word object model is a bit confusing when it comes to lists, partly because the Help documentation is decidedly nonpedagogic. To help clarify the situation, I will begin with a few remarks about lists.
List Types
There are three types of lists in Microsoft Word: single-level bulleted, single-level numbered, and multilevel outline-numbered. The term outline-numbered is a bit misleading, since an outline-numbered list can use either bullets or numbers (and need not include any numbers). Nonetheless, this is Microsoft's term, so I will use it as well.
In the case of numbered lists (single- and multilevel), there are options to either restart the numbering or continue the numbering started in a previous list. If there is text between the list you are creating and a previous list, then Word will default to new numbering. If not, Word will continue the numbering from the previous list.
For numbered lists, you can customize the numbering by setting the number format, the amount of indent, and so on. For multilevel lists, this can be done for each level separately.
What Is a List?
Word does not think of lists in the same way as we might. To Word, a document has some paragraphs that are formatted with a list format and some paragraphs that are not. These paragraphs may or may not be consecutive.
The Word object model has a List object, which you might think represents a list. However, according to the Help documentation, a List object represents "a single list format that's been applied to specified paragraphs in a document." In other words, a List object represents a list format. I will provide some examples of this in a moment.
The Word object model also has a ListFormat object, which again according to the Help documentation represents "the list formatting attributes that can be applied to the paragraphs in a range." (I discuss this object as well later in this chapter.)
To help understand how Word deals with lists, I will use the code in Example 17-1, which simply displays a message box that shows the number of paragraphs in each List object in the Lists collection (and also tells us how many List objects there are in the Lists collection).
Example 17-1: The ShowLists Procedure
Sub ShowLists() Dim i As Integer Dim s As String Dim lst As ListParagraphs s = "" For i = 1 To ActiveDocument.Lists.Count Set lst = ActiveDocument.Lists(i).Range.ListParagraphs s = s & "List " & Format$(i) & ": " & lst.Count & _ " paragraphs" & vbCrLf Next i MsgBox s End SubBulleted Lists
Let us first consider the case of bulleted lists. Consider the two short six-paragraph documents shown in Figures 17-2 and 17-3. Each document has three bulleted paragraphs, followed by a nonbulleted paragraph, followed by two additional bulleted paragraphs. It seems reasonable to say that each document has two distinct physical lists.
Figure 17-2. Document 1
![]()
Figure 17-3. Document 2
![]()
However, the ShowLists procedure displays the result shown in Figure 17-4 for Document 1 and in Figure 17-5 for Document 2.
Figure 17-4. Results of the ShowLists procedure for Document 1
![]()
Figure 17-5. Results of the ShowLists procedure for Document 2
![]()
Thus, Word considers a single bullet list format as a single List object. This is in keeping with the information in the Help documentation that says a List object represents a list format that has been applied to the document--the first document has one list format, the second has two list formats. Thus, we see that a single List object may contain paragraphs that are not contiguous.
Numbered Lists
Consider now the case of numbered lists. Figures 17-6 through 17-9 show four more documents, each with multilevel numbered lists.
Figure 17-6. Document 3
![]()
Figure 17-7. Document 4
![]()
Figure 17-8. Document 5
![]()
Figure 17-9. Document 6
![]()
In keeping with the definition of List object given in the Help documentation, it would seem that Documents 3 and 4 should each have one List object, because they have one list formatting, and Documents 5 and 6 should each have two List objects, because they have two list formats. However, Figure 17-10 shows the returns from the ShowLists procedure.
Figure 17-10. Results of the ShowLists procedure
![]()
Thus, it seems that when numbered lists are involved, it is a restarting of the numbering that determines a new List object, not a change in the list format. The upshot of this discussion is probably to heed Microsoft's own faint suggestion: "You can manipulate the individual List objects within a document, but for more precise control you should work with the ListFormat object." The fact is that List objects are useful, but you must use them with circumspection.
To illustrate this point, I recently finished a 450-page Word manuscript containing countless lists scattered throughout, with almost 300 paragraphs that are either bulleted or numbered. (In case you are interested, the manuscript is entitled Understanding Personal Computer Hardware, published by Springer-Verlag, and should be available by the time you read this. Pardon my commercial.) Here is the result of ShowLists on my manuscript:
List 1: 279 paragraphsList 2: 1 paragraphsList 3: 4 paragraphsList 4: 3 paragraphsList 5: 6 paragraphsThe first List object represents a bulleted list format, which happens to appear first on page 6 of my manuscript and last on page 416 (and in 279 paragraphs in total).
Now, the seemingly innocuous code to select the first list in the manuscript:
ActiveDocument.Lists(1).Range.Selectactually selects all text between the first and last paragraphs that have this bullet format; that is, it selects 410 pages of text! (Recall that a Range object represents a contiguous area within a document.)
Suppose that I had wanted to italicize the paragraphs in List 1. The code:
ActiveDocument.Lists(1).Range.Font.Italic = Truewould be a disaster, since it would italicize 410 pages of text. The proper code to do this job requires the ListParagraphs collection (which I discuss a bit later in this chapter):
For Each para In ActiveDocument.Lists(1).ListParagraphspara.Range.Font.Italic = TrueNext paraOne final point: two bulleted lists may look identically formatted to the naked eye, but may actually have different formatting. For instance, the list shown in Figure 17-11 is actually two List objects! (The first three paragraphs have Normal style with bulleting added, whereas the last three paragraphs are formatted with a Bullet List style.)
Figure 17-11. A document with two List objects
![]()
The List Object
The List object represents a single list format that has been applied to at least one paragraph in a document. The code:
MsgBox ActiveDocument.Lists.Countdisplays the number of List objects in the active document. Here are some of the properties of the List object:
- The ListParagraphs property
- Returns a ListParagraphs collection, which is the collection of all paragraph objects in the list that have list formatting. Note that this property also applies to the Document and Range objects, in which case some paragraphs may not have a list formatting.
- The Range property
- Returns a Range object that represents all paragraphs that lie between the first and last paragraphs in the list. This includes paragraphs that are not list-formatted. (See the earlier discussion of list objects.)
- The SingleListTemplate property
- A single list may use more than one list template; that is, it may have formatting from more than one list template. The SingleListTemplate property is True when a list uses only one list template. This is a read-only property. (I discuss list templates a bit later in this chapter.)
- The ApplyListTemplate method
- Applies a list template (that is, a list format) to the given List object. The syntax is:
ListObject.ApplyListTemplate(ListTemplate,_ContinuePreviousList)
- where ListTemplate is the list template to be applied. The optional ContinuePreviousList parameter is True to continue the previous list or False to start a new list with new numbering. (I discuss list templates a bit later in this chapter.)
- The ConvertNumbersToText method
- To convert the automatic numbering in a list to plain text numbers, we can write:
ListObject.ConvertNumbersToText
- After converting to text, the automatic numbering feature is lost.
- The CountNumberedItems method
- Returns the number of bulleted or numbered paragraphs and
LISTNUMfields in the List object. It also applies to Document and ListFormat objects:Msgbox ActiveDocument.Lists(1).CountNumberedItems
- The RemoveNumbers method
- Removes list formatting from the specified list. All indents are removed as well.
The ListTemplate Object
and ListGalleriesThe ListTemplate object represents a list template, which is an object that specifies all the formatting that defines a list. ListTemplates are kept in a ListTemplates collection.
There are three Word objects that have a ListTemplates property that returns a ListTemplates collection: Document objects, Template objects, and ListGallery objects. In other words, ListTemplate objects can be stored in any of these three objects.
ListGallery Objects
One place to keep a ListTemplate object is in a ListGallery object. There are three ListGallery objects representing the three tabs in the Bullets and Numbering dialog box. The collection of these three ListGallery objects is the ListGalleries collection.
In fact, Word supplies an enum to use as the index for the ListGalleries collection:
Enum WdListGalleryTypewdBulletGallery = 1wdNumberGallery = 2wdOutlineNumberGallery = 3End EnumThus, for instance:
ListGalleries(wdNumberGallery)is the second ListGallery object and represents the second tab in the Bullets and Numbering dialog box. Note that the type of the ListGallery object determines the type of ListTemplate objects that can be placed in that ListGallery's ListTemplates collection. For instance, the list templates in
ListGalleries(wdNumberGallery)must be single-level numbered list formats.The ListGallery object has the following properties and methods (among others):
- The ListTemplates property
- Returns a ListTemplates collection that represents all of the list formats for the ListGallery object. As mentioned, this read-only property also applies to Document and Template objects.
- The Modified property
- The Modified property has the syntax:
ListGalleryObject.Modified(Index)
- where Index is a number between 1 and 7 that specifies the position of the template in the Bullets and Numbering dialog box. (Templates are numbered from left to right, skipping the first position, marked "None".) This is a read-only Boolean property that returns True if the template in the position Index is not the built-in Word default template for that position.
- The Reset method
- Resets a ListGallery position to the default built-in template. Its syntax is:
ListGalleryObject.Reset(Index)
- where Index is a number between 1 and 7 that specifies the position of the template in the Bullets and Numbering dialog box.
The ListTemplate Object
The ListTemplate object has the following list-related properties and methods:
- The ListLevels property
- Returns the ListLevels collection, which contains ListLevel objects. (I discuss ListLevel objects a bit later in this chapter.)
- The OutlineNumbered property
- This property is True if the ListTemplate object is outline numbered (multi-level), as opposed to single-level. You can set this property to False to change a multilevel numbered list to a single-level numbered list. However, we cannot set this property for a ListTemplate object returned from a ListGallery object.
- The Convert method
- This method converts a multilevel list to a single-level list or vice versa. The syntax is:
ListTemplateObject.ConvertThe ListLevel Object
As mentioned previously, each ListTemplate object has a ListLevels collection. The ListLevel objects in this collection are the objects that really define the formatting of a ListTemplate object. This is evident by looking at the various properties of a ListLevel object, which I do later in this section.
Note first that if the ListTemplate object represents a single-level format, then its ListLevels collection contains only one ListLevel object. A multilevel list format may have up to nine levels. Hence, there may be up to nine ListLevel objects in a ListLevels collection.
Figure 17-12 shows the Customize Outline Numbered List dialog box, which contains settings corresponding to the properties of the ListLevel object. These include the following:
Figure 17-12. The Customize Outline Numbered List dialog box
![]()
- The Alignment property
- Returns or sets the alignment for the corresponding list level. Its value can be one of the constants in the
WdListLevelAlignmentenum:Enum WdListLevelAlignmentwdListLevelAlignLeft = 0wdListLevelAlignCenter = 1wdListLevelAlignRight = 2End Enum
- The Font property
- Returns or sets a Font object that represents the character formatting of the corresponding list level.
- The Index property
- This read-only property specifies the level of the ListLevel object in the ListTemplate object. Thus, if a ListLevel object has index 2, for example, then it represents the second level in the list. The value of Index is a number between 1 and 9. Note that
ListLevels(i)is the ListLevel object with index i. Thus, the index of a ListLevel object in the ListLevels collection is actually the level of the ListLevel object. (In general, the index of a member of a collection cannot be relied upon to have any particular meaning--it simply identifies the object among the members of the collection. When an object is removed from a collection, the indices of other object may change. However, this case is an exception.)- The LinkedStyle property
- Returns or sets the name of the Word style (if any) that is linked to the ListLevel object.
- The NumberFormat property
- Returns or sets a string that represents the numbering format for the list level. You can use a percent sign (%) to specify the variable that holds the numbering. For instance, to obtain the numbering format:
Section ISection IISection III. . .
- you would set the NumberFormat property as follows:
ListLevel(Index).NumberFormat = "Section %1"
- Then you would set the NumberStyle property to
wdListNumberStyle-UppercaseRoman(see the entry for the NumberStyle property later in this list).- Note that if the NumberStyle property is set to
wdListNumberStyleBullet, then the string for the NumberFormat property should contain only one character, which is used for the bullet.- The NumberPosition property
- Returns or sets the position (in points, as a Single) of the number or bullet for the ListLevel object.
- The NumberStyle property
- Returns or sets the number style for the specified object. Its value can be any of the constants in the following enum:
Enum WdListNumberStylewdListNumberStyleArabic = 0wdListNumberStyleUppercaseRoman = 1wdListNumberStyleLowercaseRoman = 2wdListNumberStyleUppercaseLetter = 3wdListNumberStyleLowercaseLetter = 4wdListNumberStyleOrdinal = 5wdListNumberStyleCardinalText = 6wdListNumberStyleOrdinalText = 7wdListNumberStyleArabicLZ = 22wdListNumberStyleBullet = 23wdListNumberStyleLegal = 253wdListNumberStyleLegalLZ = 254wdListNumberStyleNone = 255End Enum
- The ResetOnHigher property
- This Boolean property can be set to True to force the specified list level to restart its numbering (at the beginning) whenever it follows a higher list level. It can be set to False to force the numbering to continue each time the list level appears. This feature allows lists to be interleaved. The two lists in Figure 17-13 illustrate the difference, using the second list level.
Figure 17-13. Noninterleaved and interleaved lists
![]()
- The StartAt property
- Returns or sets the starting number for the specified ListLevel object.
- The TabPosition property
- Returns or sets the tab position (in points, as a Single) for the ListLevel object.
- The TextPosition property
- Returns or sets the position for the second line of wrapping text for the specified ListLevel object.
- The TrailingCharacter property
- Returns or sets the character inserted after the number for the list level. It can be any of the constants in the following enum:
Enum WdTrailingCharacterwdTrailingTab = 0wdTrailingSpace = 1wdTrailingNone = 2End EnumThe ListTemplates Collections
We have seen that ListTemplate objects can be kept in ListTemplate collections associated with either a ListGallery object, a Document object, or a Template object. Moreover, a ListGallery object contains exactly seven ListTemplate objects, which must be of a specific gallery type: bulleted, numbered single-level, or numbered outline.
The Add method can be used to create and add new ListTemplate objects to a ListTemplates collection. The syntax is:
ListTemplatesObject.Add(OutlineNumbered, Name)where OutlineNumbered is True for a multilevel list template and Name is an optional name for the list template.
Note that the Add method does not apply to a ListTemplates collection obtained from a ListGallery object. (ListGallery objects have seven list templates--period.) Indeed, you are restricted either to modifying the formatting of a ListFormat object from a list gallery or to resetting the format to its default. You cannot move, remove, or replace these ListFormat objects.
While I am on the subject, Word does not seem to have supplied any method for removing a ListTemplate object from any ListTemplates collection, so you should add ListTemplate objects carefully. Also, Microsoft does not seem to want to explain how Word itself manages the various ListTemplates collections.
If you change the formatting of an existing list in a document, Word will add a new ListTemplate to the document's ListTemplates collection. However, if you then delete the list, Word does not remove the ListTemplate object. (The document that I am using to write this book currently has 102 pages and its ListTemplates collection has a whopping 82 ListTemplate objects!)
As an example, the following code create a new ListTemplate object and adds it to the active document's ListTemplates collection. The name of the list format is "TestTemplate."
Dim lst As ListTemplateSet lst = ActiveDocument.ListTemplates.Add()With lst.OutlineNumbered = True.Name = "TestTemplate".ListLevels(1).Font.Size = 18.ListLevels(1).NumberStyle = _wdListNumberStyleCardinalText.ListLevels(2).Font.Size = 14.ListLevels(2).NumberStyle = _wdListNumberStyleCardinalText.ListLevels(3).Font.Size = 10.ListLevels(3).NumberStyle = _wdListNumberStyleCardinalTextEnd WithThe following code applies the list formatting to the currently selected list:
Selection.Range.ListFormat.ApplyListTemplate _ActiveDocument.ListTemplates("TestTemplate")Figure 17-14 shows the result of applying it to the selected text.
Figure 17-14. Applying the TestTemplate list
![]()
The ListFormat Object
The ListFormat object is a child of the Range object and is used to access various list-related properties and methods for the paragraphs in a given range. In a sense, it is the conduit to the list-related properties of a range. The ListFormat object for a given range is obtained through the ListFormat property, as in:
rng.ListFormatThe Apply...Default methods
The ApplyBulletDefault, ApplyNumberDefault, and ApplyOutlineNumberDefault methods apply (or remove) the default bulleted, numbered, or outline numbered format to the specified range. If the range is already so formatted, the formatting is removed.
Unfortunately, Microsoft does not make clear what is meant by the default list format for each type, but it appears to be the format that was last used to apply list formatting of that type. In any case, to apply a specific list format, you can use the ApplyListTemplate method, described next.
The ApplyListTemplate method
This method applies a list formatting from a specified ListTemplate object to the specified range. The syntax is:
ListFormatObject.ApplyListTemplate(ListTemplate, _ContinuePreviousList, ApplyTo)where ListTemplate is the ListTemplate object to be applied, the optional ContinuePreviousList is set to True to continue the numbering from the previous list or False to start new numbering, and the optional ApplyTo is set to one of the constants in the following enum:
Enum WdListApplyTowdListApplyToWholeList = 0wdListApplyToThisPointForward = 1wdListApplyToSelection = 2End EnumFor example, the following code applies the sixth bullet format in the Bullet List Gallery to the selected text:
Selection.Range.ListFormat.ApplyListTemplate _ListGalleries(wdBulletGallery).ListTemplates(6)The ConvertNumbersToText method
This method changes the list numbers and optionally any
LISTNUMfields in the specified range into text. After invoking this method, automatic list numbering is no longer available. The syntax is:ListFormatObject.ConvertNumbersToText(NumberType)where the optional parameter NumberType specifies the type of number to be converted and can be one of the constants in the following enum:
Enum WdNumberTypewdNumberParagraph = 1wdNumberListNum = 2wdNumberAllNumbers = 3End EnumThe constant
wdNumberParagraphrefers to the automatic numbering that is applied via a list format. The constantwdListNumrefers to anyLISTNUMfields that are included in the list.The ConvertToNumbers method is useful when exporting a Word document to a format that does not support automatic list numbering (such as rich text format), but we want to keep the existing numbering.
The CountNumberedItems method
This method returns the number of bulleted or numbered items (and
LISTNUMfields) in the specified range. (The method also applies to Document and List objects.) Its syntax is:ListFormatObject.CountNumberedItems(NumberType, Level)where NumberType is the same as in the ConvertToNumbers method and Level is an optional parameter that specifies the level for which you want to apply the method. If Level is omitted, all levels are counted.
The List property
If each paragraph in the specified range lies within a single List object, the List property returns that List object. It is read-only. As an example, consider the four-line document shown in Figure 17-15.
Figure 17-15. A simple four-line document
![]()
If I highlight the second and third lines, for instance, then the code:
MsgBox Selection.Range.ListFormat.List.CountNumberedItemsreturns the number 3, since the List object contains three numbered lines. However, if I highlight the second, third, and fourth lines, the code produces an error message, since the specified range (that is, the selection) is not contained completely within a single List object, and so:
Selection.Range.ListFormat.Listis set to
Nothing. Thus, to avoid errors, it is important to explicitly test forNothing, as in:If Not Selection.Range.ListFormat.List Is Nothing ThenMsgBox Selection.Range.ListFormat.List.CountNumberedItemsEnd IfThe ListIndent, ListOutdent, and ListLevelNumber methods
The ListIndent method increases the list level of the paragraphs in the specified range by one level. Its syntax is:
ListFormatObject.ListIndentThe ListIndent method makes the most sense when applied to a multilevel list. For instance, if the insertion point is on the second line in the multilevel list shown in Figure 17-16, then the code:
Selection.Range.ListFormat.ListIndentresults in the list shown in Figure 17-17.
Figure 17-16. A list before calling the ListIndent method
![]()
Figure 17-17. The same list after calling the ListIndent method
![]()
The ListOutdent method is similar to the ListIndent method, but decreases the list level instead of increasing it. The syntax is:
ListFormatObject.ListOutdentThe ListLevelNumber property returns or sets the list level (as a Long integer) for the first paragraph in the specified range.
The ListString and ListValue properties
The read-only ListString property returns a string that shows how the list numbering appears for the first paragraph in the specified range. For example, if the second line is the list in Figure 17-18 is selected, the code:
Selection.Range.ListFormat.ListStringreturns the string "Two.".
Figure 17-18. A three-item list
![]()
Note that for bulleted lists, the previous code returns the ANSI character code for the bullet, but you must apply the correct font (which is usually Symbol or Wingdings) to see the actual bullet.
The read-only ListValue property returns the numeric value corresponding to the string value returned by the ListString property. For instance, consider the list in Figure 17-19. If the insertion pointer is on the line with the text "line3," then the ListValue property is equal to 2, because "b" is the second item in the alphabet.
If the paragraph in question has a bulleted format, then ListValue returns 1.
Figure 17-19. A nested list
![]()
The ListTemplate property
If each paragraph in the specified range is formatted with a single ListTemplate object, the ListTemplate property returns that ListTemplate object. This is a read-only property.
The ListType property
This property returns the type of lists that are contained in the specified range. It can be any one of the constants in the following enum:
Enum WdListTypewdListNoNumbering = 0wdListListNumOnly = 1wdListBullet = 2wdListSimpleNumbering = 3wdListOutlineNumbering = 4wdListMixedNumbering = 5End EnumFor instance, if I highlight the two lists shown in Figure 17-20, then the code:
Selection.Range.ListFormat.ListTypereturns the number 5 (or
wdListMixedNumbering).
Figure 17-20. Lists of two different types
![]()
The RemoveNumbers method
This method removes numbers or bullets from the specified range. The syntax is:
ListFormatObject.RemoveNumbers(NumberType)where NumberType is one of the constants in the following enum:
Enum WdNumberTypewdNumberParagraph = 1wdNumberListNum = 2wdNumberAllNumbers = 3End EnumSee the ConvertNumbersToText method for more on these constants.
The SingleList property
This read-only property is True if the specified range contains only one List object.
The SingleListTemplate property
This read-only property is True if the entire range uses a single list template.
Example: Looking at Lists
By way of example, let me share with you an issue I had to deal with recently concerning the 450-page manuscript I mentioned earlier. After sending the manuscript to the publisher, the editor requested that I add a half-line spacing between each bulleted item to improve readability. This was easy enough to do--I just changed the List Bullet style that I used to create bulleted lists. However, having worked with computers for some time, I knew enough to check each list visually to make sure everything came out as expected.
Now, it is easy to iterate through each list paragraph in the document. The following code will do the job:
Dim para As ParagraphFor Each para in ActiveDocument.ListParagraphspara.Range.SelectNext paraThe problem here is that the list paragraphs will go flying by without allowing me to check each paragraph.
The solution is to write a macro that uses a static variable that remembers the index of the current list paragraph. (Recall that a static variable retains its value as long as the document to which the code is attached is open or as long as there is an open document that uses the template to which the code is attached.) Then, when the macro is executed repeatedly, it goes from one list to the next. By assigning a hotkey or a toolbar button to this macro, it is easy to check each list paragraph. The code is shown in Example 17-2.
Example 17-2: Iterating List Paragraphs
Sub FindNextList() Dim i As Integer Static Idx As Integer ' Idx is initialized to 0! If Idx >= ActiveDocument.ListParagraphs.Count Then MsgBox "Done" Exit Sub End If ActiveDocument.ListParagraphs(Idx + 1).Range.Select Idx = Idx + 1 End SubNote the use of the index
Idxin the preceding code. I had to make some adjustments because an integer variable is initialized to 0, but the ListParagraphs collection is 1-based.
Back to: Writing Word Macros
© 2001, O'Reilly & Associates, Inc.