I spend half the day creating the same type of document over and over again. I've streamlined things as much as possible with templates, AutoText, and AutoCorrect, but it's still duller than watching asphalt dry.
This sounds like a job for a macro or two. In short, you switch on the Macro Recorder, perform the series of actions that you want to be able to repeat at the touch of a button, and then stop the Macro Recorder. You can then play back the macro as often as needed. Here's how it works:
Start Word and set up the document you want to automate.
Choose Tools Macro Record New Macro to display the Record Macro dialog box (see Figure 8-1). Type a name for the macro (no spaces, but underscores are okay) in the "Macro name box and provide a description in the "Description" box. In the "Store macro in" drop-down list, make sure that "All Documents (Normal.dot)" is selected, unless you specifically want to store the macro in a different document or template. (If you do, the macro will be available only when that document, that template, or a document based on that template is open .)
Click the OK button to start recording the macro. Word displays the Stop Recording toolbar, which contains a Stop Recording button and a Pause Recording/Resume Recording button. The mouse pointer displays an audio-cassette icon to indicate that recording is on.
Perform the actions that you want the macro to repeat. You can perform almost all keyboard actionsfor example, moving the insertion point and selecting objectsand you can execute most commands from the menus . You cannot use the mouse to select objects or to drag the selection, although you can use the context menu to execute commands (for example, Cut or Paste).
If you need to perform an action that you don't want to record in the macro, click the Pause Recorder button on the Stop Recording toolbar, perform the action, and then click the Resume Recorder button.
When you've finished all the actions you want to record, click the Stop Recording button or double-click the REC indicator on the status bar to stop recording. (You can also double-click this indicator to start recording a macro.)
To play back the macro, open or activate the document you want to affect, choose Tools Macro Macros, select the macro in the listbox, and click the Run button. If you cant see the macro in the list, check the setting in the "Macros in" drop-down list. Choose "All active templates and documents" to see the full list of available macros.
Hey, that was easy! I recorded a long macro that sets up the tedious bits of the proposal to a client. The trouble is, I got so carried away that I saved the document and closed it without stopping the Macro Recorder, so now each time I run the macro, it tries to save the active document under the same name.
Right: time for you to bite the bullet and come to grips with the Visual Basic Editor. Don't worry, it doesn't bite; it tries to be friendly, and it threatens you with neither AutoCorrect nor AutoFormat As You Type.
You can quickly toggle into the Visual Basic Editor by pressing Alt+F11 from Word, but it's usually best to display the Macro dialog box, select the macro you want to edit, and then click the Edit button. That makes the Visual Basic Editor open the macro you want to edit, so you don't have to navigate to reach it. Figure 8-2 shows the Visual Basic Editor with a recorded macro open.
In the upper-left corner of the Visual Basic Editor window is the Project Explorer, which you use to navigate among open projects. A project is the VBA component of a document or template. Word places all recorded macros in the NewMacros module in the Normal projectthe VBA component of Normal.dot . A module is a container for VBA code.
Below the Project Explorer is the Properties window, which shows the available properties (attributes) of the selected item. In this case, the NewMacros module is selected, and the only available property is its (Name) property.
The main area of the Visual Basic Editor is the Code window, which shows either the code for the selected item (here, the NewMacros module) or the selected user form. (A user form is a custom dialog box.) Each macro begins with a Sub line that shows the name of the macro followed by empty parenthesesfor example, Sub Format_MD_Report() and ends with an End Sub line.
To change your recorded macro, you edit the appropriate commands in the Code window. In this case, look for the commands for saving the document (the lines starting with ActiveDocument.SaveAs ; an underscore at the end of a line indicates that the following line is a continuation) and closing it (the ActiveDocument.Close command). Select these lines using the mouse or the keyboard, and press the Delete key to delete them.
If you're satisfied with your changes, save them (click the Save button on the Standard toolbar, press Ctrl+S, or choose File Save), and then choose File Close and Return to Microsoft Word to close the Visual Basic Editor and return to Word. Alternatively, press Alt+F11 to flip back to Word, leaving the Visual Basic Editor open so that you can return to it later if you need to make further changes.
Press Enter to create a new line, press to move back to the new line, and type the code for a message box (see Figure 8-3), which should look like this:
If MsgBox(Prompt:="Format this document?", Buttons:=vbYesNo + vbQuestion, _ Title:="Format MD Report") = vbNo Then Exit Sub End If
This MsgBox statement uses three arguments, named items that denote information used in the statement: Prompt , Buttons , and Title . Prompt receives a text string in double quotation marks (here, "Format this document" ), which is displayed in the body of the message box. Title receives another string ( "Format MD Report" ), which appears in the titlebar. The Buttons argument controls both the buttons displayed in the message box and the icon (if any). vbYesNo makes the message box display a Yes button and a No button. vbQuestion makes the message box display a question-mark icon.
The If condition makes VBA check which button the user clicks in the message box. If the user clicks the No button, the result of the message box is vbNo . In this case, the Then statement comes into effect, and the Exit Sub command makes VBA exit the subprocedurein other words, skip the rest of the code in the macro. If the user clicks the Yes button, the result of the message box is vbYes ; the Exit Sub command doesn't run in this case, so the rest of the macro does run. The End If statement marks the end of the If condition.
As you type the code for the message box, the Visual Basic Editor will help you out with prompts. When the Visual Basic Editor displays a drop-down list of possible options, you can choose from it by "typing down" (continuing typing) to reach your selection, by using and , or by using the mouse.
To test your message box, step into the macro by clicking anywhere between the Sub and End Sub lines and then pressing F8 to execute one command at a time. The Visual Basic Editor displays a yellow highlight on the command it's currently executing so you can track what's happening. When the message box is displayed, click the Yes button or the No button to dismiss it so you can continue executing the code.
I began word processing using a Mac application called WriteNow. When you saved a document in which you were working, WriteNow also saved where you were (that is, where your cursor was positioned in the document). When you opened the document again to continue, the cursor started off where you had left it at the end of the previous session. It frustrates me with Word, especially with a loooong document, to always find myself back at the first character of the file when I reopen it.
A macro can make Word return to the last editing position. If you assign the macro the predefined name AutoOpen , it will run automatically when you open a document.
PREDEFINED MACRO NAMESWord recognizes five predefined names for macros that run automatically:
By assigning these names, you can make macros run automatically. You can also make an automatic macro run another macro if needed. To do so, include the other macro's name on its own line. For example: Sub AutoOpen() Configure_Document_for_Editing End Sub |
Here's how to create the macro:
Open the Visual Basic Editor and create a new module for storing your macros. (Word puts recorded macros in the NewMacros module by default, but you'll do better to keep your macros separate.) Right-click the Normal item in the Project Explorer and choose Insert Module. The Visual Basic Editor creates a new module called Module1 . Press F4 to move the focus to the Properties window, type a distinctive name for the module (again, no spaces, but underscores are okay), and press Enter to apply it.
Press Enter at the end of the line. The Visual Basic Editor automatically adds the parentheses after the macro name, a blank line, and the End Sub line:
Sub AutoOpen() End Sub
Type the statement Application.GoBack between the Sub line and the End Sub line.
It's as easy as that. If you don't always want to return to the last editing position when you open a document, add a message box to let you choose whether to do so, as shown in Example 8-1.
Sub AutoOpen() If MsgBox(Prompt:="Return to the last edit?", Buttons:=vbYesNo + vbQuestion, _ Title:="Return to Last Edit") Then Application.GoBack End If End Sub
Call me weird, but I've taken to editing in Print Preview. But it takes three steps to switch to Print Preview, enter Edit mode, and zoom the window how I want it. I'd like to make it one step.
Easy enough. Actually, this is a good candidate for a recorded macro. Choose Tools Macro Record Macro, type a name (for example, PrintPreviewEdit ), type a description, and then click the OK button. Choose File Print Preview, and click the Magnifier button to enter editing mode. Choose View Toolbars Print Preview to toggle off the Print Preview toolbar (to save the space it takes up). Choose View Zoom, select your preferred zoom settings, and click the OK button. Click the Stop Recording button on the Stop Recording toolbar to stop the Macro Recorder.
If you open the macro in the Visual Basic Editor, it should look something like Example 8-2.
Here's what's going on in this macro:
The ActiveDocument.PrintPreview statement technically applies the PrintPreview method to the ActiveDocument object. The ActiveDocument object represents the active documentthat is, the document you're working with in the user interface. A method is a command or action that you can take with an object.
The CommandBars("Print Preview").Visible = False statement turns off the display of the Print Preview toolbar. A CommandBar object is either a toolbar (as it is here) or a menu bar. VBA groups similar objects into groups called collections to simplify access to them. To reach an object in a collection, you specify its name (as here: the object called "Print Preview" in the CommandBars collection) or its index number (for example, CommandBars(1) indicates the object with the index number 1 in the CommandBars collection). Objects have properties (attributes) that control how they behave. This statement sets the Visible property of the CommandBar object to False , making it invisiblein other words, hiding the toolbar.
With... End With is a structure for applying multiple methods , properties, or a combination of the two to the same object. By using a With statement, you tell VBA that you're referring to a particular object until you start referring to another object. (This enables VBA to work faster than if it has to work out the target object each time.) This With statement works with the Zoom object contained in the View object within the ActivePane object (the active pane) within the ActiveWindow object. The statements contained in the With statement set the number of page columns to 2 and the number of page rows to 1 in other words, the equivalent of clicking the Many Pages button in the Zoom dialog box and choosing two pages wide by one page deep.
Sub PrintPreviewEdit() ' PrintPreviewEdit Macro ' Macro recorded 5/14/2005 by Teresa Ramirez ActiveDocument.PrintPreview ActiveDocument.ActiveWindow.View.Magnifier = False CommandBars("Print Preview").Visible = False With ActiveWindow.ActivePane.View.Zoom .PageColumns = 2 .PageRows = 1 End With End Sub
Word opens in Outline view with everything expanded, including the text. (See "Expand and Collapse Outline View Quickly" in Chapter 3 for more on this annoyance.) I only want it to show Level 1 headings.
Create the macro shown in Example 8-3 to replace Word's built-in ViewOutlineMaster command, which is used for the View Outline command and for the Outline View button on the horizontal scrollbar.
Sub ViewOutlineMaster() ActiveWindow.View.Type = wdOutlineView ActiveWindow.View.ShowHeading (1) End Sub
The first statement changes the view of the active window to Outline view. The second statement collapses the outline so that it shows only Level 1 headings. Change the heading number if you want to display a different level of headingsfor example, ActiveWindow.View.ShowHeading (3) displays Level 1, Level 2, and Level 3 headings.
Copy and paste the macro in the same module you created in "Return to the Last Editing Position When Opening a Document," but change the name of the copy to "ViewOutline." This replaces the built-in Word command assigned to the Ctrl+Alt+O shortcut for switching to Outline view. (If you need to get the built-in Word command back, rename or delete your macro.)
Still in the Visual Basic Editor, click the Save button, or choose File Save, to save your changes.
I receive lots of documents that I need to read but that I'm not supposed to change. I know I could open them as read-only from the Open dialog box by using the drop-down on the Open button, but I usually open them directly from email or from Explorer, so I don't have that choice. What I'd like is a box to check that tells Word I want to handle a document as "read only" after I've already opened it.
As you say, there's no simple checkboxor other controlto switch an open document to read-only status. But you can create a macro that does this well enough (see Example 8-4). Also, from Windows Explorer, you can right-click a Word document and choose "Open as Read-Only" from the shortcut menu.
Example 8-4 probably looks impenetrable, but once you get the hang of the If... Then statements, it's pretty straightforward. Here's what's happening:
Sub Reopen_Active_Document_As_Read_Only() 'close the active document and reopen it as read-only 'store the page number and return to it when the document is reopened Dim strDocName As String Dim strDocFullName As String Dim strTitle As String Dim intPage As Integer strDocName = ActiveDocument.Name strDocFullName = ActiveDocument.FullName strTitle = "Reopen Document As Read-Only" intPage = Selection.Information(wdActiveEndPageNumber) If MsgBox(Prompt:="Close " & strDocName & " and reopen as read-only?", _ Buttons:=vbYesNo + vbQuestion, Title:=strTitle) = vbYes Then If ActiveDocument.Path = "" Then MsgBox Prompt:="This document has never been saved." & vbCr & vbCr _ & "Please save it now.", _ Buttons:=vbOKOnly + vbExclamation, Title:=strTitle If Dialogs(wdDialogFileSaveAs).Show = 0 Then Exit Sub End If strDocFullName = ActiveDocument.FullName End If If Not ActiveDocument.Saved Then If MsgBox(Prompt:="The document contains unsaved changes." & vbCr _ & vbCr & "Click Yes to save the changes; " & _ "click No to discard the changes.", Buttons:=vbYesNo + vbQuestion, _ Title:=strTitle) = vbYes Then _ ActiveDocument.Save End If ActiveDocument.Close SaveChanges:=wdDoNotSaveChanges Documents.Open FileName:=strDocFullName, ReadOnly:=True Selection.GoTo What:=wdGoToPage, Which:=wdGoToAbsolute, Count:=intPage End If End Sub
The first few lines are comments explaining what the macro does.
The second group of lines uses Dim statements to declare variables , or storage slots for data used in the macro. The format is Dim variablename As variabletype . The first three declarations create String variables, which are variables that can hold text characters . The fifth declaration creates an Integer variable, which is a variable that can contain only a whole number (either positive or negative).
The third group of lines assigns information to the variables. The strDocName variable receives the document's name (without the path). The strDocFullName variable receives the document's name and path. The strTitle variable receives the text to be displayed in the titlebar of each message box. The intPage variable gets the page number of the end of the current selection. (This will be used for the macro to display this page again after reopening the document.)
The first If statement displays a message box to confirm that the user wants to run the macro. If the user clicks the Yes button, returning the value vbYes , the rest of the macro runs; if not, execution branches to the End If statement just before the End Sub statement, and the macro ends without taking any action.
Provided the user has clicked the Yes button in the first message box, the next If statement checks whether the active document's path is an empty string ( "" double quotation marks with no characters between them). If so, that means the document has never been saved, so the macro displays a notification message box (with just an OK button) followed by the Save As dialog box, which is the object named wdDialogFileSaveAs in the Dialogs collection. If the value returned by the Save As dialog box is , the user has clicked the Cancel button rather than saving the document, so the Exit Sub statement exits the macro. Otherwise , the strDocFullName = ActiveDocument.FullName statement assigns the document's name and path (now that it has one) to the strDocFullName variable.
The next If statement checks whether the document contains unsaved changes ( If Not ActiveDocument.Saved translates to "if the active document has not been saved") and, if so, displays a message box asking the user to click the Yes button to save the changes or the No button to discard the changes. If the user clicks the Yes button, the ActiveDocument.Save statement saves the changes.
After all those preliminaries , it's time for action. The ActiveDocument.Close SaveChanges:=wdDoNotSaveChanges statement closes the document without saving changes. (If the user chose to save unsaved changes, they've already been saved.) The Documents.Open statement then uses the strDocFullName variable to open the document again, this time as read-only. The Selection.GoTo statement displays the page whose number is stored in the intPage variablethat is, the page that was displayed when the macro was run.
I tend to end up with a stack of documents open and need to close all except the one I'm working on. Clicking the Close button gets tedious, even though it could hardly be easier.
You can easily create a macro (see Example 8-5) to close all the documents except the current one. To do so, you use a loop , a structure that tells VBA to repeat an action as long as a condition is true or until a condition is met.
Sub Close_All_Except_Active_Document() Dim i As Integer Dim strKeepOpen As String strKeepOpen = ActiveDocument.Name For i = Documents.Count To 1 Step -1 If Documents(i).Name <> strKeepOpen Then Documents(i).Close Next i End Sub
As you can see, this macro is short and friendly: it even lacks comments about what the macro does (add them at the beginning if you like) and a message box to confirm that the user wants to take the action (you could add this too). Here's how it works:
The first group of lines declares an Integer variable named i and a String variable called strKeepOpen . The next line then assigns to strKeepOpen the name of the active document, which is the document the macro will leave open.
The For... Next loop uses the variable i as its counter and runs from Documents.Count (which returns the number of open documents) to 1 , decrementing the counter by 1 ( Step -1 ) at each iteration. (By default, loops increment the counter by one unless you specify otherwise. This loop needs to run backwards because each time the macro closes a document, the number of documents in the Documents collection decreases by one.) The If statement compares the name of the current document with strKeepOpen and closes the document if the name doesn't match. For example, if you have 12 documents open, Word starts with Documents(12) the twelfth document in the Documents collectionand closes that document if its name does not match the value of strKeepOpen . The Next i statement indicates the end of the loop, making execution return to the For statement. The loop continues to execute until the counter reaches 1.
I often have to create documents that are cobbled together from various sources, and I need to strip out all the existing formatting.
As you saw in Chapter 3, pasting material as unformatted text can save you plenty of formatting annoyances. To automate the process, all you need is a single line of VBA code (see Example 8-6).
Sub Paste_As_Unformatted_Text() Selection.PasteSpecial Link:=False, DataType:=wdPasteText, _ Placement:=wdInLine, DisplayAsIcon:=False End Sub
Our systems people have customized our main work template so that we can all use company-standard keyboard shortcuts for applying styles and running macros. The problem is that I was ahead of them on this and have been using my own shortcutswhich aren't all the same as the new ones. So now some of my shortcuts work, while others run commands I'm not expecting. How can I see what's assigned where? I'm sick of combing through the Customize Keyboard dialog box in the hope of identifying the apparently random command they've assigned to my shortcut.
Good newsyou can print out a list of assignments. Choose Tools Macro Macros, type listcommands , and press Enter or click the Run button. In the List Commands dialog box (see Figure 8-4), choose the "Current menu and keyboard settings option (to see the full list of commands, choose the "All Word commands" option). You can print out the resulting table for reference, search through it, or sort it by the column that interests you (using Table Sort).
My boss emails me numbered lists of tasks for everyone in the office. When I put them into our Assignment template, I need to delete all the numbers he's typed in, because the lists get automatic numbering from the styles. I know I can go through selecting each list and then clicking the Numbering button on the Formatting toolbar twice, once to remove the numbers and once to reapply them, but that messes up the list templates.
Create the short macro shown in Example 8-7 to remove the manually applied numbers from a selected list.
WordBasic was the programming language used in early versions of Word, before Microsoft applied VBA to Word. VBA includes a WordBasic object that enables you to use WordBasic commands, such as this one, via VBA.
Sub Remove_Manual_List_Numbering() WordBasic.ToolsBulletsNumbers Replace:=0, Type:=1, Remove:=1 End Sub
The Title Case option in the Change Case dialog box (Format Change Case) makes the first letter of each word uppercase and the remaining letters lowercase. But conjunctions and short prepositions (five letters or fewer, according to the magazine I work for) should be all lowercase.
A macro can fix most of these errors, although you will need to check prepositions that need initial capitals because they are part of a verb. Example 8-8 shows the macro.
The code in the macro is fairly straightforward:
In the first group of statements, the macro declares two variables: a String variable named strL and an Integer variable named i .
In the second group of statements, the macro selects the appropriate paragraph. The first statement compares the selection type to wdInsertionPoint the "selection" being a point in the text rather than an actual selection of characters or other objects. If the selection is not an insertion point, the macro collapses the selection to an insertion point. The second statement selects the first paragraph of the ( collapsed ) selectionin other words, the paragraph in which the insertion point is located. Selecting the paragraph also selects the paragraph mark at its end, and this paragraph mark counts as a "word." So, to work with only the real words, the third statement shortens the selection by one character to the left, deselecting the paragraph mark.
The third group of statements applies title case (first letter capitalized) to the first and last words in the selection, because the first and last words in a heading must always be capitalized even if they're short prepositions or conjunctions.
The fourth group of statements uses a For... Next loop to check each of the remaining words in the selection against a list of short prepositions and conjunctions. (Adjust this list to meet your company's style demands.) The loop runs from 2 (the second word) to Selection.Words.Count - 1 , or the number of words in the paragraph minus 1 (so as not to affect the last word, to which title case has already been applied). The second statement assigns to strL the lowercase ( LCase ) version of the currently selected word, with any leading or trailing spaces trimmed off it (when you double-click to select a word, VBA includes any spaces after the word). Using the lowercase version of the word is necessary because the comparison is case-sensitive. The If statement then compares strL to the list of prepositions and conjunctions. If there's a match, Word applies lowercase to the word; if not, the Else statement applies title case to the word.
Finally, the Selection.Collapse statement collapses the selection to its end, which is equivalent to pressing to deselect a selection.
Sub Apply_Proper_Capitalization() 'applies proper capitalization to the first paragraph in the selection Dim strL As String Dim i As Integer If Selection.Type <> wdSelectionIP Then Selection.Collapse Selection.Paragraphs(1).Range.Select Selection.MoveLeft Unit:=wdCharacter, Count:=1, Extend:=wdExtend Selection.Words(1).Case = wdTitleWord Selection.Words(Selection.Words.Count).Case = wdTitleWord For i = 2 To Selection.Words.Count - 1 strL = LCase(Trim(Selection.Words(i))) If strL = "a" Or strL = "above" Or strL = "after" Or strL = "an" Or _ strL = "and" Or strL = "as" Or strL = "at" Or strL = "below" Or _ strL = "but" Or strL = "by" Or strL = "down" Or strL = "for" Or _ strL = "from" Or strL = "in" Or strL = "into" Or strL = "of" Or _ strL = "off" Or strL = "on" Or strL = "onto" Or strL = "or" Or _ strL = "out" Or strL = "over" Or strL = "the" Or strL = "to" Or _ strL = "under" Or strL = "up" Or strL = "with" Then Selection.Words(i).Case = wdLowerCase Else Selection.Words(i).Case = wdTitleWord End If Next i Selection.Collapse Direction:=wdEnd End Sub
Call me paranoid , but I think Word may really be out to get meor perhaps it's just that I'm working with master documents. Anyway, Word seems unstable, and I want to keep a backup copy of each document I'm working with while I'm working with it. It seems to me that Word's automatic backup copy would be just about ideal, if it were a copy of my most recent save rather than the last-but-one save. With the speed at which I'm trying to make progress on my documents, even losing a couple of minutes' worth of changes is painful.
Create a macro to force Word to save the document twice in immediate succession. Call the macro FileSave so that it replaces Word's built-in Save command and picks up its keyboard shortcut (Ctrl+S).
If you simply want to double-save the document, all you need is the macro shown in Example 8-9.
Sub FileSave() Options.CreateBackup = True ActiveDocument.Save ActiveDocument.Saved = False ActiveDocument.Save End Sub
The Options.CreateBackup = True statement ensures that the "Always create backup copy" feature is on (you can also set it by choosing Tools Options, clicking the Save tab, and checking the "Always create backup copy box).
But if you really need to safeguard your work, you can adapt the macro so that it automatically copies the backup document to a safe locationfor example, a network drive, as in Example 8-10.
This macro declares three String variables to store the text it uses for naming the documents. It assigns to strDocName the name of the active document and assigns to strWordBackupDoc a string consisting of the full name (including the path) that Word gives the backup document for the active document. For example, if the active document is named Example.doc and is located in the C:\Samples folder, its backup document is named C:\Samples\Backup of Example.wbk .
The macro assigns to strMyBackupDoc the path and name for the backup document that you want to create. In this example, the macro assigns the backup document the path Z:\Publi-c\Backups\ (change it as needed, and use the notation \\server\folder\ if the drive isn't mapped to a letter), the basic filename, the word "backup," the date in yyyy-mm-dd format, and the time in hh-mm-ss AMPM format. In other words, the backup document for Example.doc will have a name such as Example backup 2005-05-27 04-14-38 PM.doc .
Finally, the FileCopy statement copies the file specified by strWordBackupDoc to strMyBackupDoc . If there's already a file with that name in the backup folder, it will be overwritten, but unless you save twice within a second, this shouldn't happen.
Sub FileSave() Dim strDocName As String Dim strWordBackupDoc As String Dim strMyBackupDoc As String Options.CreateBackup = True ActiveDocument.Save ActiveDocument.Saved = False ActiveDocument.Save strDocName = ActiveDocument.Name strWordBackupDoc = ActiveDocument.Path & "\Backup of " & _ Left(strDocName, Len(strDocName) - 3) & "wbk" strMyBackupDoc = "Z:\Public\Backups\" & Left(strDocName, _ Len(strDocName) - 4) & " backup " & Format(Date, "yyyy-mm-dd") & _ " " & Format(Time, "hh-mm-ss AMPM") & ".doc" FileCopy strWordBackupDoc, strMyBackupDoc End Sub
Changing the Compatibility settings in the Options dialog box so that I can legibly print a document that contains color text on my monochrome printer (see "Print Colored Text as Black on a Monochrome Printer" in Chapter 6) is getting kinda old. Automate it for me already!
Right you are. Create a macro that looks like Example 8-11.
This macro creates two Boolean variables, which are variables that can only be true or False . It then checks to see if the number of open documents is ; if so, it stops running the macro so as to avoid the error that results from trying to print without a document open.
Sub Print_Active_Document_in_Monochrome() Dim blnBWOn As Boolean Dim blnDocClean As Boolean If Documents.Count = 0 Then Exit Sub With ActiveDocument blnDocClean = .Saved blnBWOn = .Compatibility(wdPrintColBlack) .Compatibility(wdPrintColBlack) = True Dialogs(wdDialogFilePrint).Show .Compatibility(wdPrintColBlack) = blnBWOn .Saved = blnDocClean End With End Sub
For the bulk of its code, the macro uses a With statement referring to the active document. It stores the current state of the document's Saved property ( TRue if the document contains no unsaved changes, False if it does contain some) in the blnDocClean variable, and the state of the Compatibility (wdPrintColBlack) setting (whether the "Print colors as black on noncolor printers" checkbox on the Compatibility tab of the Options dialog box is checked) in the blnBWOn variable.
The macro then sets Compatibility(wdPrintColBlack) to true and displays the Print dialog box so that the user can print the document. Finally, the macro restores Compatibility(wdPrintColBlack) and Saved to the values stored in the two variables.
When I'm working on a document, I often want to finish editing a page, print it, check it, and then print another page. But the Print dialog box always appears with the All option selected in the Page Range group box.
With a short macro (see Example 8-12), you can make the Print dialog box appear with the Current Page option, the Pages option, or the Selection option selected instead. If you select the Pages option, you can specify the range of pages to print, as in the example.
Sub Display_Print_Dialog_Custom() With Dialogs(wdDialogFilePrint) .Range = wdPrintRangeOfPages .Pages = "3,5,7-11" If .Show = -1 Then .Execute End With End Sub
The With statement works with the wdDialogFilePrint member of the Dialogs collectionin other words, the Print dialog box. The commands inside the With statement control the options selected:
To select the Current Page option, use this statement:
.Range = wdPrintCurrentPage
To select the Pages option and specify a simple range of pages, use these statements, substituting suitable numbers on the From and To lines:
.Range = wdPrintFromTo .From = "2" .To = "4"
To select the Pages option and specify a complex range of pages, use these statements, substituting suitable numbers on the Pages line:
.Range = wdPrintRangeOfPages .Pages = "3,5,7-11"
The Show method displays the dialog box and returns a value for the button the user clicks. If the value is -1 , which indicates that the user clicked the OK button, the Execute command executes the instructions contained in the dialog box. If the user clicks the Cancel button, VBA does not execute the instructions.
I tried the replace-with-subscript trick mentioned in Chapter 4 (see "Replace with a Subscript"), and it's fine as far as it goes. But what I need to do is find each table caption; check to see if the paragraph before it uses a boxed style; and, if it doesn't, apply a top border to the table caption. I can't do that with Replace, now can I?
Not as you describe it, no. But a macro using Find can locate the table captions, check them, and apply the border as needed. The macro shown in Example 8-13 searches for each paragraph using the Table Caption style and applies a top border to that paragraph if the previous paragraph doesn't have a bottom border. You'll need to adapt the specifics, but the principle should work for you.
Sub Check_Table_Captions() 'go through the document for paragraphs in the Table Caption style 'check if the paragraph before has a bottom border 'if not, apply a top border to the Table Caption paragraph Dim i As Integer Dim strMTitle As String On Error GoTo ErrorHandler strMTitle = "Apply Upper Border to Table Caption Style" If MsgBox(Prompt:="Apply the upper borders to the Table Caption style?", _ Buttons:=vbYesNo + vbQuestion, Title:=strMTitle) = vbNo Then Exit Sub End If For i = 2 To ActiveDocument.Paragraphs.Count With ActiveDocument.Paragraphs(i) .Range.Select If .Style = "Table_Caption" Then If .Previous.Borders(wdBorderBottom).LineStyle <> _ wdLineStyleSingle Then .Borders(wdBorderTop).LineStyle = wdLineStyleSingle .Borders(wdBorderTop).LineWidth = wdLineWidth100pt .Borders(wdBorderTop).Color = wdColorBlack End If End If End With Next i ErrorHandler: If Err.Number = 5834 Then MsgBox Prompt:="This document doesn't use the Table Caption style.", _ Buttons:=vbOKOnly + vbInformation, Title:=strMTitle Exit Sub Else MsgBox Prompt:="The following error has occurred:" & vbCr & vbCr & _ "Error Number: " & Err.Number & vbCr & vbCr & _ "Error Description: " & Err.Description, _ Buttons:=vbOKOnly + vbInformation, Title:=strMTitle Exit Sub End If End Sub
Much of this macro is straightforward if you've been following along through this chapter, but the following points are worth noting:
The On Error GoTo ErrorHandler line tells VBA to trap errors and directs them to an error handler (a section of code designed to deal with errors). Unhandled errors in your macros usually display error message boxes, which are confusing for users, as many of the errors are hard to interpret. The error this macro is most likely to encounter is the active document not containing the Table Caption style. If this happens, the code after the ErrorHandler: label at the end of the macro runs, checking the error number against the number for a missing style ( 5834 ); if it matches, the error handler displays a message box explaining what has happened and then exits the macro. If there's a different error, the Else statement makes the macro display a message box giving the error's number and description. This information tends to be neither intelligible nor helpful, but it's preferable to having an error message box named Microsoft Visual Basic appear unexpectedly on the screen.
The For loop uses a counter ( i ) and runs from i = 2 to ActiveDocument.Paragraphs.Count , which is the number of paragraphs in the document. This loop enables the macro to check every paragraph in the document (skipping the first paragraph, which doesn't have a paragraph before it) for the Table Caption style.
The With statement works with the paragraph the loop is currently examining ( ActiveDocument.Paragraphs(i) ). If the paragraph's style is Table Caption, the nested If statement checks to see if the previous paragraph ( .Previous ) is lacking a bottom border. If it is, the three .Borders statements apply the necessary border.
I need a printout of all the fonts that Word can offer me. I'm sick of scrolling through the font list trying to find one that looks okay.
Try the macro shown in Example 8-14.
Here's what the macro does:
First, it declares two String variables, one to store the current font name and the other to store the sample text string.
It then displays an input box prompting the user to enter the sample text string that she wants to use, providing her with default text that she can accept. If the input box returns an empty string ( "" ), the user either clicked the Cancel button, or deleted the default string and failed to replace it. Either way, the macro quits without further ado.
Next, the macro creates a new document ( Documents.Add ) and uses a For... Next loop that runs once for each font (i.e., for each item in the FontNames collection). The loop resets the font, types the font's name followed by "parahere," applies the font, and types the sample text string. The word "parahere" is a placeholder that enables the macro to separate the font names from the sample text that follows ; you can use any other unique text string instead.
The macro then selects all the content of the document and sorts it alphabetically by paragraph.
The With Selection.Find structure clears formatting and options that might cause problems and then performs two replace operations. Some Find properties may already be set in Word, so it's best to clear formatting, specify a zero-length text string, and turn off options such as "Match case," "Use wildcards," and "Find all word forms." The Execute command executes each search. The first replace operation replaces the placeholder text "parahere" with "parahere" and an extra paragraph, which splits each font name from its sample text. The second replace operation replaces "parahere" with a Heading 2 paragraph. This has the effect of applying the Heading 2 style to the paragraphs that contain the font names.
The three Selection statements collapse the selection, apply the Heading 1 style to the first paragraph in the document, and enter a heading for the list in that paragraph.
The message box tells the user what the macro has done and prompts her to save the document if she wants to keep it.
Sub List_All_Fonts() 'Lists all the fonts available to Word with the current printer driver Dim strFont As Variant Dim strText As String strText = InputBox(Prompt:= _ "Type the sample text you want to use in the font listing:", _ Title:="List All Fonts", _ Default:="The five boxing wizards jump quickly.") If strText = "" Then Exit Sub Documents.Add For Each strFont In FontNames Selection.Font.Reset Selection.TypeText strFont & "parahere" Selection.Font.Name = strFont Selection.TypeText strText & vbCr Next ActiveDocument.Content.Select Selection.Sort FieldNumber:="Paragraphs", _ SortFieldtype:=wdSortFieldAlphanumeric With Selection.Find .ClearFormatting .MatchCase = False .MatchAllWordForms = False .MatchWholeWord = False .MatchSoundsLike = False .MatchWildcards = False .Forward = True .Wrap = wdFindContinue .Text = "parahere" .Replacement.Text = "parahere^p" .Execute Replace:=wdReplaceAll .Text = "parahere" .Replacement.Style = "Heading 2" .Execute Replace:=wdReplaceAll .Replacement.ClearFormatting .Replacement.Text = "" .Execute Replace:=wdReplaceAll .Text = "" End With Selection.Collapse Direction:=wdCollapseStart Selection.Paragraphs(1).Style = "Heading 1" Selection.TypeText "List of Fonts Available to Word" MsgBox "The macro has created a list showing the fonts currently " _ & "available to Word." & vbCr & vbCr & _ "Please save this document if you want to keep it.", _ vbOKOnly + vbInformation, "List All Fonts Macro" End Sub