Programming Publisher 2003 Made Easy: Lesson 4

 

Andrew May
Microsoft Corporation

October 2004

Applies to:
    Microsoft Office Publisher 2003

Summary: Learn how easy it is to use the Publisher object model to write macros that automate tasks in Publisher and save you time and energy. This lesson builds on previous lessons and introduces several techniques to make your macros more flexible and powerful: object collections and their properties and methods, the For Each. . .Next programming loop, and how to nest and indent code statements. (13 printed pages)

Contents

Introduction
Collections
Using For Each...Next Loops
Exercise
Review
Conclusion
Additional Resources

Introduction

Welcome back!

In this lesson, we discuss a few more advanced aspects of object-oriented programming, techniques that build on what we already learned about programming the Publisher object model. We review another special type of object, the collection. Then we introduce another programming logic statement, the For Each. . .Next statement, and show how you can use these statements with collections to affect multiple objects with just a few lines of code.

Finally, we show you how to insert If. . .Then and For Each. . .Next statements within each other to create more responsive and flexible macros.

Collections

For the past few lessons, we worked with objects. We even showed how the forms and controls you use to create dialog boxes are special types of objects. Now, it's time to introduce one last special type of object: the collection.

**Review   **An object is a model of a real-world thing. Objects have properties, methods, and events. A property is a characteristic of an object. A method is something an object can do. An event is something that happens that an object can react to.

A collection is an object that holds a group of other objects of a specific type. So, for example, if Car is an object, Cars is a collection, a collection of cars.

**Tip   **Collections are usually named the plural of whatever type of object they hold. So for a collection of Car objects, Cars is the logical collection name.

We actually were already working with collections. For example, the Pages collection contains one Page object for each page in the publication; the Shapes collection contains one Shape object for each shape on a specific page.

A collection can also act as a property of another object. Continuing with the car example, cars have doors. So a Doors collection contains a group of Door objects. The Doors collection can also act as a property of the Car object. Therefore, the whole relationship is this:

The Cars collection contains a group of Car objects. Each Car object has a property called Doors. The Doors collection contains a collection of Door objects.

Methods and Properties of Collections

All collection objects have methods and properties that allow you to access, or use, the individual objects they contain. Almost all collections have three important properties and methods: a Count property, an Item method, and some type of Add method. Let's take a minute and discuss what each one of these does, and why it's important.

Using the Count Property

The Count property tells you how many objects are currently in the collection. The Count property changes as objects are added or deleted from the collection. A collection can be empty, in which case the Count property is zero. For example, for a page with no shapes on it, the Shapes.Count property equals zero.

The Pages collection has a Count property that indicates how many pages are currently in a publication. The following macro uses that property to display a message box that shows the number of pages in the publication.

Sub DisplayNumberofPages()
  MsgBox "This publication has " & ActiveDocument.Pages.Count & " pages."
End Sub

****Note   MsgBox is a special Microsoft Visual Basic procedure that displays a message box to the user. You can specify the text you want to display in the message box as a parameter of the Msgbox procedure, as we do in the above example.

Notice how the number displayed by the macro changes as you add or delete pages.

Using the Item Method

You can use the Item method of a collection to access a specific object within that collection. In most collections, the Item method takes one parameter, and that's the number of the object in the collection. For example, in the following code statement, the second page in the active publication is assigned to the variable myPage.

Dim myPage as Page
Set myPage = ActiveDocument.Pages.Item(2)

**Terms   **The number of an object in a collection is also sometimes referred to as its index.

To take our car example to absurd lengths, suppose we have a parking lot full of cars, and we want to open our car door. First, we'd need to select our car from the collection of cars in the parking lot. Then, we'd need to specify a particular door on that car, and open it. We'd be using the two collections we mentioned earlier: Cars and Doors. Our fictitious code might look something like this:

Dim myCar as Car
Set myCar = ParkingLot.Cars.Item(3)
myCar.Doors(1).Open

For a more real-world example, take a look at the illustration below. The red arrow points to the third shape on the first page of a publication. This example also uses two collections we saw previously: the Pages collection and the Shapes collection.

Figure 1. Specifying a shape on a page

You can also use a data variable as a parameter for the Item method, if the data variable is an integer. Remember, data variables hold data, and using them is the same as using the data you stored in them. For example, look at the following code:

Dim numberofPages As Integer
Dim theLastPage As Page
numberofPages = ActiveDocument.Pages.Count
Set theLastPage = ActiveDocument.Pages.Item(numberofPages)

Here, you set the variable numberofPages to contain the number of Page objects in the Pages collection. Then you set your theLastPage object variable to the final Page object in the collection. So, if there were five pages in the publication, then the last line of code is equivalent to writing this:

Set theLastPage = ActiveDocument.Pages(5)

Selecting the Right Object in a Collection

Before you specify an object in the collection, it's good to know how the objects in the collection are numbered. Different types of collections may number their objects in a different way. For example, the Page objects in the Pages collection are numbered in the order they appear in the publication. So you know that the third page in a publication is always Document.Pages.Item(3). However, the Shape objects in a Shapes collection are numbered in the order in which you put them on the page, not based on their position on the page. So the shape in the upper-left corner may be Shapes.Item(1), or Shapes.Item(4), or whatever, depending on how many shapes you put on the page before it.

If you have any doubt about how the objects in a collection are numbered, see the Microsoft Office Publisher 2003 Visual Basic for Applications Reference. We'll show you how in our next lesson.

Using the Add Method

Add methods allow you to add objects to a collection. This example adds a page to the active publication:

Dim newPage As Page
Set newPage = ActiveDocument.Pages.Add(1, 2)

How you use the Add method depends on the collection. The Add method of one collection may have different parameters than the Add method of another collection. In the above example, we use two parameters. The first tells Publisher the number of pages to add (1), and the second tells the page we want to insert the new page after (2). These parameters wouldn't make any sense for an Add method of the Shapes collection.

Review   ****Some methods have parameters, which give you choices for what you want the method to do. When a method has more than one parameter, you separate the parameters with a comma.

In fact, some collections may have more than one Add method. The Shapes collection has many, depending on which particular type of Shape object you want to add to the collection. Here's just a few:

  • AddCurve
  • AddLine
  • AddPicture
  • AddTable
  • AddWebNavigationBar

As you can guess, most of these methods have different parameters, based on the specific type of object they add to the collection.

In our next lesson, we'll show you how to use the online Visual Basic for Applications (VBA) help to find out what parameters a method takes.

One last thing to keep in mind about data variables: Once you store information in the variable, that information doesn't change unless you change it somehow. So, for example, if you set a data variable equal to an object property, and you later change the value of the object property, you haven't changed the value of the data variable. Data variables stay the same until you change them directly.

Note   ****Be aware that this is not the case with most object variables, however. For example, suppose you set an object variable equal to a certain shape in the publication. If you then change that shape's fill color, that change is reflected in the object variable as well.

With that in mind, see if you can answer the questions in this lesson's Pop Quiz.

Pop Quiz

Take a look at the following fictitious code and answer the questions that follow. Assume that at the start, the Cars collection is empty, so its Count property is equal to 0. Step through the code line-by-line and keep track of the numberofCars variable and the objects in the Cars collection.

Dim numberofCars as Integer
Set car1 = ParkingLot.Cars.Add("Gremlin")
Set car2 = ParkingLot.Cars.Add("Pinto")
numberofCars = ParkingLot.Cars.Count
Set car3 = ParkingLot.Cars.Add("Ford Intimidator SUV")
Set car4 = ParkingLot.Cars.Item(numberofCars)
Set car5 = ParkingLot.Cars.Item(numberofCars + 1)
  1. What is the value of the data variable numberofCars?
  2. What is the value of car3?
  3. What is the value of car4?
  4. What is the value of car5?

Look for the answers at the end of this lesson.

Using For Each...Next Loops

Now we're going to introduce another type of code statement, like the If. . .Then statement we covered in the last lesson. The For Each. . .Next code statement lets you perform some action for each individual object in a collection. To explain what we mean more clearly, consider the following example:

Let's say you've got several publications in which all the text is formatted in the "Times New Transylvanian Semi Italic Condensed" font. You're ready to send the publications off to the printer when you realize there's one problem: You don't have a license to distribute the "Times New Transylvanian Semi Italic Condensed" font, and the printer doesn't have that font. So you have to change the font. Everywhere. Sure, you could go through every page of every file, select every shape, and change the font manually. But wouldn't it be great if you could write a few lines of code that would do all that drudge work for you?

And, thanks to your newly-acquired programming skills, you can. You can create a macro that uses a For Each. . .Next loop to do it all for you, like so:

Sub FormatText()
  Dim myShape As Shape
  
  For Each myShape In ActiveDocument.Pages.Item(1).Shapes
    myShape.TextFrame.TextRange.Font.Name = "Arial"
  Next myShape
End Sub

So here's what actually happens when Publisher reaches the first line of the For Each. . .Next line. Publisher stores the first object in the Shapes collection in the myShape object variable. Then, Publisher runs the code statements in the For Each. . .Next statement. In this case, that code statement sets the font of the myShape variable to Arial. So this sets the font of the first shape in the Shapes collection to Arial. Next, Publisher stores the second object in the Shapes collection in the myShape object variable, and repeats the code statement within the loop. This sets the font of the second object to Arial. Publisher repeats this process with each object in the collection. By the time it's done, all the shapes in the Shapes collection have their font set to Arial.

This is called a loop because Publisher actually runs these lines of code once for each object in the collection. So if there are five items in the collection, Publisher runs the code five times. In each loop, Publisher stores the first object in the collection into the object variable, and then runs the code inside the For Each. . .Next statements. Then it stores the next object from the collection into the object variable and runs the same code. And so on, until it's used every object in the collection.

Note   ****While most do, not all collections have this ability enabled.

To create a For Each. . .Next Loop

  1. Open Publisher and the Visual Basic Editor. In the Code window, create a new procedure or place the cursor in an existing procedure. In the procedure, declare an object variable of the object type contained in the collection, like so:

    Dim ObjectVariable as ObjectType
    
  2. Create the For Each. . .Next loop using the following pattern:

    For Each ObjectVariable in ObjectCollection
       Code statements
    Next ObjectVariable
    

    When Publisher comes to the For Each. . .Next loop, it stores the first object in the collection in the object variable, and then runs the code statements inside the For Each. . .Next loop. Then it places the second object in the object variable, and again runs the code statements, and so on, once for each object in the collection.

    This also works if there are no objects in the collection. In that case, the code inside the For Each. . .Next loop doesn't run at all.

Now, this macro is a real time-saver as far as it goes, but if you read the code closely, you'll see the For Each. . .Next loop only applies to the shapes on page one: ActiveDocument.Pages.Item(1).Shapes. To perform the required formatting on each page, we'll have to uses a technique called nesting.

Nesting Code Statements

The term nesting simply refers to the practice of placing one code statement or loop inside another. You can include programming logic loops within other loops, such as an If. . .Then within a For Each. . .Next loop. In fact, you can nest statements as deep as you want; Publisher keeps track of it all, and runs the code in the order it reads it. The only rule is that the entire code statement must be placed within the other code statement. For example, this code would work fine:

For Each aCar in ParkingLot.Cars
  If aCar.Running Then
    aCar.Horn.Honk
  End If
Next aCar

However, Publisher wouldn't be able to run this code:

For Each aCar in ParkingLot.Cars
  If aCar.Running Then
    aCar.Horn.Honk
Next aCar
  End If

In this second example, the last line of the If. . .Then statement is placed outside the For Each. . .Next statement, which contains it. This produces an error when Publisher attempts to run the code.

So let's return to our earlier example, the one that formats all the text as Arial on the first page of the active publication. We want the code to format the text on every page in the publication, not just the first. And we don't know how many pages each publication may have. Here's what we can do: We're going to add another For Each. . .Next loop. This loop selects each Page in the Pages collection. Then, we place our first For Each. . .Next loop, the one that selects each shape in the Shapes collection, inside the new loop. So our finished macro looks like this:

Sub FormatText()
  Dim myPage As Page
  Dim myShape As shape
  
  For Each myPage In ActiveDocument.Pages
    For Each myShape In myPage.Shapes
      myShape.TextFrame.TextRange.Font.Name = "Arial"
    Next myShape
  Next myPage
End Sub

Here's what happens when Publisher runs this macro:

  • When Publisher reaches the first For Each. . .Next loop, it stores the first Page object in the myPage variable.
  • Then, it runs the second, nested For Each. . .Next loop. As it performs this loop, it stores each Shape in the Shapes collection on page one in the myShape variable, and formats the shape text to Arial.
  • When it's finished looping through all the Shape objects in the Shapes collection on page one, Publisher returns to the outer loop. It stores the second page in the myPage variable.
  • Publisher runs the second, nested For Each. . .Next statement again, formatting the text of each shape on page two as Arial.
  • Publisher repeats this loop for each Page object in the Pages collection, that is, each page in the publication.

You can see how nesting one code loop within another can really up the power and flexibility of your macros.

Using Indenting to Improve the Clarity of Your Code

Once you get to the point where you're nesting code statements, it's a really good idea to make sure you indent the code you write. The rule for indenting is really easy: Each time you write code that is nested inside other code, you indent another level. That way, you can tell just from glancing at the code which statements are nested inside which.

Now, you don't have to indent; it doesn't affect how Publisher runs the code at all. It's there for your sake, to help you understand the order in which Publisher runs the code statements. Let's take another look at that macro, only this time without the indentation:

Sub FormatText()
Dim myPage As Page
Dim myShape As shape
For Each myPage In ActiveDocument.Pages
For Each myShape In myPage.Shapes
myShape.TextFrame.TextRange.Font.Name = "Arial"
Next myShape
Next myPage
End Sub

It's quite a bit more difficult to figure out which statements are nested inside which, and therefore the order in which the code runs, isn't it? Now imagine this as just one part of a larger, more complicated macro. Without indenting, things can get pretty ugly pretty quick.

Exercise

Now let's put all of this lesson's concepts together in a real-world example.

Your company has a new logo. A bright, shiny new logo. Which is wonderful, except that you have dozens of publications that use the old logo: order forms, marketing brochures and flyers, business cards, the works. But never fear; using the programming skills you've developed in this lesson, we know you can create a macro that does the heavy lifting for you.

Let's start by defining what we really want the macro to do. We want it to look at every shape on every page in the active publication, and, if the shape is a linked picture of the old company logo, then replace it with the new logo. Now, let's break those directions down into a series of individual tasks that we can write as code.

First, we know we need to look for the logo on every page. So we can start by writing a For Each. . .Next loop that selects each page:

For Each pbPage In ActiveDocument.Pages

Next pbPage

On each page, we need to determine if each shape is the logo. So we can write another For Each. . .Next loop, one that selects each shape. We place that loop within the first, like this:

For Each pbPage In ActiveDocument.Pages
  For Each pbShape In pbPage.Shapes

  Next pbShape
Next pbPage

Next, we write an If. . .Then statement to determine if each shape is a linked picture:

For Each pbPage In ActiveDocument.Pages
  For Each pbShape In pbPage.Shapes
    If pbShape.Type = pbLinkedPicture Then

    End If
  Next pbShape
Next pbPage

And finally, we need a second If. . .Then statement. This one determines if the picture linked to is the logo image. If it is, the code statement within the If. . .Then statement replaces it with the new logo image.

So our finished macro looks like this:

Sub UpdateLogo()
Dim pbPage As Page
Dim pbShape As shape

For Each pbPage In ActiveDocument.Pages
  For Each pbShape In pbPage.Shapes
    If pbShape.Type = pbLinkedPicture Then
      If pbShape.PictureFormat.Filename = "C:/logo.gif" Then
        pbShape.PictureFormat.Replace ("C:/newlogo.gif")
      End If
    End If
  Next pbShape
Next pbPage
           
End Sub

As you can probably tell, this example assumes that the old company logo is a gif named logo.gif, and the new company logo is a gif named newlogo.gif, and that both are located on your C:/ drive. You can change the name of the files or the location where Publisher looks for them by simply changing "C:/logo.gif" or "C:/newlogo.gif" where they appear in the code.

To test this code, pick a picture and insert it (as a linked picture) on several pages of the publication in which you wrote this macro. Change "C:/logo.gif" in the macro code to the name and location of that linked picture. Then pick a picture you want to replace it with. Change "C:/newlogo.gif" to the name and location of the second picture.

Review

A collection is an object that holds a group of other objects of a specific type. So, for example, if Car is an object, Cars is a collection, a collection of cars.

A collection can also act as a property of another object.

All collection objects have methods and properties that allow you to access, or use, the individual objects they contain. Almost all collections have three important properties and methods: a Count property, an Item method, and some kind of Add method.

  • The Count property tells you how many objects are currently in the collection. The Count property changes as objects are added or deleted from the collection.
  • Use the Item method of a collection to access a specific object within that collection. In most collections, the Item method takes one parameter: the number of the object in the collection.
  • Add methods allow you to add objects to a collection. How you use an Add method depends on the collection. The Add method of one collection may have different parameters than the Add method of another collection.

The For Each. . .Next code statement lets you perform some action for each individual object in a collection. To create a For Each. . .Next loop:

  1. Declare an object variable of the object type contained in the collection:

    Dim ObjectVariable as ObjectType
    
  2. Create the For Each. . .Next loop using the following pattern:

    For Each ObjectVariable in ObjectCollection
       Code statements
    Next ObjectVariable
    

The term nesting refers to the practice of placing one code statement or loop inside another. You can include programming logic loops within other loops, such as an If. . .Then statement within a For Each. . .Next loop.

It's considered a best practice to indent the code you write. The rule for indenting is: Each time you write code that is nested inside other code, you indent another level. Indenting helps you tell which statements are nested inside which. However, you don't have to indent; it has no affect on how Publisher runs the code.

Answers: Pop Quiz

Here's the code again:

Dim numberofCars as Integer
Set car1 = ParkingLot.Cars.Add("Gremlin")
Set car2 = ParkingLot.Cars.Add("Pinto")
numberofCars = ParkingLot.Cars.Count
Set car3 = ParkingLot.Cars.Add("Ford Intimidator SUV")
Set car4 = ParkingLot.Cars.Item(numberofCars)
Set car5 = ParkingLot.Cars.Item(numberofCars + 1)

And here are the questions and answers:

  1. What is the value of the data variable numberofCars?

    The answer is: 2. Because there are only two Car objects in the collection when the code sets the value of the numberofCars variable, the variable value is 2. That doesn't change as we add more Car objects. However, the value of the Cars.Count property does increase with each new Car object.

  2. What is the value of car3?

    Answer: Ford Intimidator SUV.

  3. What is the value of car4?

    Answer: Pinto. As we saw, the numberofCars variable equals 2. The code sets car4 to the second Car object in the collection, which is the Pinto. In this case, this line of code:

    Set car4 = ParkingLot.Cars.Add(numberofCars)
    

    Is equivalent to writing this line of code:

    Set car4 = ParkingLot.Cars.Add(2)
    
  4. What is the value of car5?

    Answer: Ford Intimidator SUV. Because using a data variable is the same as using the information in the variable, Publisher treats the statement numberofCars + 1 as equivalent to 2 + 1, or 3. So in this case the code sets car5 to the third Car in the Cars collection, the Ford Intimidator SUV.

Conclusion

By now you've developed quite a range of skills and techniques for programming Publisher. At the end of four lessons, your toolbox includes: objects, including special types like forms, controls, and collections; object properties, methods, and events; data types; object and data variables; and programming logic like If. . .Then and For Each. . .Next statements.

In our next, and final, lesson, we'll answer the question: Where do you go from here? We'll take an in-depth tour of the online help available. We'll show how you can use it to get the information you need about the Publisher object model to write your macros.

See you then.

Acknowledgments

Special thanks go out to Frank Rice, for allowing me to steal the structure of his Super-Easy Guide to the Microsoft Office Excel 2003 Object Model article (along with an example or two) for this series of lessons. Thank you, Frank.

Additional Resources

This section lists a number of resources you can use to learn more about the products and technologies mentioned or used in this article.

Sites:

Articles:

Blogs: