List pages to print, select one page

B

BruceM

I have a situation where the user may select one of several possible pages
to print. My idea (which may be off the mark, but it's what has occurred to
me) is that each of the pages would contain a bookmark. I have adapted some
code Jay Freedman posted about a year ago to list bookmarks. I run the code
as a macro.

Dim bkm As Word.Bookmark
Dim doc As Word.Document
Dim strBkm As String
Set doc = ActiveDocument
strBkm = ""

For Each bkm In doc.Bookmarks
strBkm = strBkm & bkm.Name & vbCrLf
Next

MsgBox strBkm

This produces a list of bookmarks in the document. I realize the list as
produced by this code is for display only, but what I hope is that the user
can select from the list, thereby printing the page on which the bookmark
appears. I already have worked out some code that can identify the page
number. Maybe it would be something like:

Dim b1 as Long

b1 = ActiveDocument.Bookmarks("MoreStart").Range _
.Information(wdActiveEndAdjustedPageNumber)
ActiveDocument.PrintOut _
Range:=wdPrintFromTo, From:=CStr(b1), To:=CStr(b1)

This may not be the smoothest way to accomplish what I need. For instance,
maybe I can produce a list of bookmarks, and when the user selects one Word
can go to that page, and I can use wdPrintSelection, or something like that.
Or maybe there is another option I have not considered.

The main thing is that I would first need to have a user-selectable list to
identify the page that needs to be printed.
 
G

Greg Maxey

Bruce,

You probably don't want to clutter you list of pages the user can select
from with any other bookmark name. One way to do this is with a UserForm.

Create a UserForm that contains a ListBox that list the pages the User can
choose from (I would make it a multi-select list) and a Command Button.

Populate the Listbox with the list of pages bookmarked "Page_1", Page_2",
etc.

Here is some sample code that you could use:

Private Sub CommandButton1_Click()
Dim i As Long
Dim pStr As String
If ListBox1.ListIndex <> -1 Then
For i = 0 To ListBox1.ListCount - 1
If ListBox1.Selected(i) Then
pStr = pStr + ListBox1.List(i) & ", "
End If
Next i
End If
'Clean up string
pStr = Left(pStr, Len(pStr) - 2)
'PrintOut
Application.PrintOut Range:=wdPrintRangeOfPages, Pages:=pStr
Unload Me
End Sub

Private Sub UserForm_Initialize()
Dim oBM As Bookmark
Dim oBMs As Bookmarks
Set oBMs = ActiveDocument.Bookmarks
For Each oBM In oBMs
If InStr(oBM.Name, "Page_") > 0 Then
Me.ListBox1.AddItem Mid(oBM.Name, 6, Len(oBM.Name) - 5)
End If
Next
End Sub
 
B

BruceM

Thanks for the reply. I am not very familiar with user forms, but I did
manage to create one and add a list box. In many cases the user will not
print the selected pages, but will only view the document. Right now the
user navigates to the required page and clicks a custom toolbar item to run
a macro, causing the current page to print. The Help documentation,
however, is vague at best about how to actually open or run a userform.

After poking around some more I managed to create a userform with a list
box, and to populate the list box with selected bookmarks. The user form is
opened when a macro is run. I used the list box After Update event to print
the page on which the selected bookmark is located. First I call the user
form (named fusrBkm) from a macro:
fusrBkm.Show
I do it this way because the situation is somewhat more complex than I have
shown: one or more pages other than the one with the selected bookmark may
need to be printed. The macro identifies other bookmarks and sets the print
range(s) accordingly.

Other code (the list box is lstBkm):

Private Sub UserForm_Initialize()

Dim oBM As Bookmark
Dim oBMs As Bookmarks

Set oBMs = ActiveDocument.Bookmarks

For Each oBM In oBMs
If InStr(oBM.Name, "_") <> 0 Then
Me.lstBkm.AddItem (oBM.Name)
End If
Next

End Sub

I read several times in my research that a DocVariable may be used instead
of a bookmark. I thought that if I could do that I wouldn't need to worry
about filtering the list of bookmarks, but I could not figure out how to
list DocVariables in the list box. Since I don't even know if it's possible,
I didn't spend that much time trying.

Private Sub lstBkm_AfterUpdate()

Dim i As Long
Dim strBkm As String
Dim pStr As String

If fusrBkm.lstBkm.ListIndex <> -1 Then
For i = 0 To fusrBkm.lstBkm.ListCount - 1
If fusrBkm.lstBkm.Selected(i) Then
pStr = pStr + fusrBkm.lstBkm.List(i) & ", "
End If
Next i
End If
'Clean up string
pStr = Left(pStr, Len(pStr) - 2)

'PrintOut
strBkm =
ActiveDocument.Bookmarks(pStr).Range.Information(wdActiveEndAdjustedPageNumber)
Application.PrintOut Range:=wdPrintFromTo, From:=strBkm, To:=strBkm

Unload Me

I could not get the ListIndex or any other properties for lstBkm unless I
drilled down through the userform name. Also, I kept getting an error
(invaalid print range, I think) when I tried:
Application.PrintOut Range:=wdPrintRangeOfPages, Pages:=pStr

Anyhow, it seems to work. I would be interested in clearing up the
remaining questions, but it seems to be something I can work with. Thanks
again.
 
G

Greg Maxey

Bruce,

I am not completely sure that I know what your ulitimate goal is. The code
sent to you was tied to a document that had a bookmark name "Page_1" on page
1, Page_2 on page 2, etc. There was also the possibility that other
bookmarks where present in the document.

The UserForm initialize code strips out "Page_" from the bookmark name and
populates the Listbox with 1, 2, 3, etc.

The ListBox was a multi-select box so if the user wants to print pages 1, 3,
and 5 then they would select those pages and then press the command button
to print.

Using the AfterUpdate event like you do would make a dog's breakfast out of
that approach ;-)

To populate a listbox with Variables you have to first create the variables
e.g.,

Sub CreateSomeDocVariables()
Dim oVar As Variables
Set oVar = ActiveDocument.Variables
oVar("1-3").Value = "1-3"
oVar("4").Value = "4"
oVar("6-9").Value = "6-9"
End Sub

And then populate the listbox:

Private Sub UserForm_Initialize()
Dim oVar As Variable
Dim oVars As Variables
Set oVars = ActiveDocument.Variables
For Each oVar In oVars
Me.ListBox1.AddItem oVar.Name
Next
End Sub


Private Sub UserForm_Initialize()
Dim oVar As Variable
Dim oVars As Variables
Set oVars = ActiveDocument.Variables
For Each oVar In oVars
Me.ListBox1.AddItem oVar.Name
Next
End Sub
 
B

BruceM

The document is a technical package of work instructions, including a
routing slip (one page of the document with the manufacturing steps
summarized). Most technical packages have only one routing slip. However,
some packages have a choice of routing slips. I understand what you are
saying about the multi-select list box. If it is needed I can add a command
button, but as it stands only one routing slip will be selected. However,
it could be that a command button is more comfortable for the user, since
they will have a chance to take another look at what they have selected
before printing it. The code would be the same as in the After Update
event. Other pages, such as a log for measurements, etc., will be printed
in every case, so there is no user input.

I have realized that some of the code assumes a multi-select list box, and
that with a single-select list box it can be simplified:
i = fusrBkm.lstBkm.ListIndex
pStr = fusrBkm.lstBkm.List(i)

Sorry I didn't describe my needs more clearly from the first, but I will say
that the multi-select code is now part of my code library. I expect I will
use it one day.

Regarding the use of variables, I had thought it was a matter of inserting a
DocVariable field, then referencing that field. My early experiments showed
me that it is not that simple. The Initialize code you provided (and which
I had tried on my own before the previous post) produces an empty list.

I have done some more research, and have discovered that I can list the
fields by way of:
Dim fldVar As Field

For Each fldVar In ActiveDocument.Fields
If fldVar.Type = wdFieldDocVariable Then
' Parse the field to remove field type, etc.
End If
Next fldVar

I would parse the field to make it more user-friendly in the list. However,
I suppose I would need to reassemble it in order to find the page number, if
it is possible at all to find the page on which a field is located. I see
no advantage over bookmarks, even if I can get it to work. It's interesting
to experiment with it, but I think it is outside this project's scope. It
seems to do the trick as it is, so it's probably time to move on with other
projects.

Thanks for all of your help with this.
 
G

Greg Maxey

Bruce,

Yes I would use a command button. I think most people would find it odd for
the forms to immediately begin printing on a click in the listbox. In fact,
you might save paper if you added a confirmation message box in the command
button code that told the User what pages where about to be printed and
offer an OK/Cancel options.

I would just use:

If lstBkm.ListIndex <> -1 Then
pStr = lstBkm
End If

.... as the simplified code for defining the initial pStr.

The part I am still missing is what exactly to you want to appear in the
Listbox? Do you want something like:

Label: Chose Pages to print:
1
2-3
5
6-9

and are these values fixed?

If so, I don't see why you need bookmarks or anything else in the document
itself.
 
B

BruceM

I think I will use a command button, as you suggest, for the reasons you
mention.

The bookmarks (or something) are needed to identify the page. The page
number alone is not enough information, unless the user is going to scroll
around in the document to identify the page number. For instance, one
routing slip may be Apply Coating. Another would be Coat and Polish. Then
maybe there is an appendix: Strip Coating. The list box would show:
Apply Coating
Coat and Polish
Strip Coating

Of course, the bookmarks have underscores instead of spaces, but I replace
them with spaces for the list box, then reverse the replacement so the code
can identify the page number.

If the document contains only one routing slip, the user runs a macro (by
way of a toolbar button) that prints everything from the bookmark DocStart
to the bookmark DocEnd. If a document contains more than one routing slip,
so that a choice needs to be made, the existence of a ChoosePage bookmark
brings up the user form.
 
G

Greg Maxey

Bruce,

I suppose my remaining question is: Do you know the page range of "Apply
Coating," "Strip Coating," etc.?

If so then I still don't see the need for Bookmarks or anything else in the
document.

If you (as the document author) know the page number of the various routing
slips then build the Listbox list in the initialize procedure

and then in the command button procedure use the user selection to define a
string that makes sense to the PrintOut method.

Dim printStr As String
Select Case Me.lstBkm
Case Is = "Apply Coating"
printString = "1-3"
Case Is = "Strip Coating"
printString = "6, 8-9"
Case Else
MsgBox "You didn't pick a routing slip to print"
Exit Sub
End Select
Application.PrintOut Range:=wdPrintRangeOfPages, Pages:=pStr
Unload Me

If I am missing your mark then just ignore my comments has hapless
ramblings.
 
B

BruceM

I need to apply the code to many documents. I gave a simplified example
with Apply Coating and Strip Coating. The technical packages are for a
diverse range of processes on a wide assortment of parts. Depending on the
customer, one technical package may be for several dozen parts. They are
grouped in categories, but one type of part may require certain processes.
Another group of the same type of part may require something like an
additional coating on a portion of the part.
Without going into a lot of details about that, even if two technical
packages have Strip Coating and Apply Coating routing slips, there is no
guarantee they are on the same page. This is due in some cases to the fact
that different people over the years have developed the technical packages,
and in other cases to different requirements from customer to customer.
Bookmarks have the advantage of allowing me to add them to the document and
then not worry about the coding. One disadvantage is that I need to come up
with a naming convention that will allow me to filter the list to just the
ones that need to appear in the list box. Right now I am using underscores,
but I may end up adding something such as R__ to the start of the list
bookmarks, and test for that before adding it to the list box. I can remove
unnecessary characters for display, then add them back later, which is what
I am doing now with the underscores. However, underscores are common in
bookmarks, so another type of "flag" may be a better choice in order to
avoid extra items on the list.
In investigating bookmarks I looked up some information on hidden bookmarks,
which I gather are parts of cross-references, although I can't seem to
stumble upon it now. With the Word Help dialog being a Microsoft creation
it is all but useless for specific searches such as "hidden bookmark". I
wonder if there is some coherent information somewhere about such things as
hidden bookmarks and cross-reference fields. They are potentially useful,
although my early experiments are not encouraging. I can insert a
cross-reference as a hyperlink, but I can't make it look like a hyperlink,
and I can't seem to control the text that is displayed. I realize this is
OT relative to the original post, but as I said it came up, and now I am
curious as to whether I can put the information and techniques to use.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top