Multiple Legacy Forms

J

Jim G

I am attempting to create a proposal document for my company. When preparing
the proposal I would like for the project managers to see 50 or so options
that can be included on the project. Perhaps bullets or drop down for those
options that are to be shown on the formal proposal. Assuming the project
manager wants to only show 15 of the 50 options, I want these 15 selected
fields to merge on to another word document. I've setup legacy drop downs,
but realized you cannot select multiple options. I also do not wish to
create the 50 options that can be included on the proposal and shown yes/no
for all. I only wish for the bullet selected fields to merge or create an
IF/THEN scenario. Perhaps the form field is not the best option and I was
wondering if anyone had any suggestions on how to get what I need from one
..docx or .xlsx to another. I'm running 2007 Small Business.

Thanks
 
J

Jay Freedman

Hi Jim,

You're right, a form field is not a good option, both because they don't do
multiple selection, and because they're limited to at most 25 entries.

You could program a userform in the macro editor
(http://support.microsoft.com/?kbid=306258), which would solve both of those
problems -- the list box in a userform can be set to allow multiple
selections, it can display check boxes to the left of the items, and its
capacity is limited only by memory availability. Having gotten the selection
part of the userform, though, you'd also have to program the part that
includes the required text in the document (or excludes the text that isn't
required).

If you don't have programming experience, look at a commercial solution such
as DataPrompter (http://www.wordsite.com/products/dpdas.htm).

--
Regards,
Jay Freedman
Microsoft Word MVP
Email cannot be acknowledged; please post all follow-ups to the newsgroup so
all may benefit.
 
J

Jim G

Thanks Jay!

I gave myself a crash course in programming this morning and picked up most
of the beginner commands fairly easily from the support link you provided.
I'm running Word 2007, but most of the commands are the same as the 2002
support link.

I did have one hiccup I'm hoping someone can assist with fixing. I had no
problem creating the visual basic form and module, but I must have missed a
step. Once I inputted this on the .docm as a text form field under the
legacy tools button everything works if I only needed this macro once.
Assuming I wanted someone to peruse this drop down and select 15 items of the
50 or if I wanted to insert this form in the document more than once, the
support link I believe is setup to program a one and done. If I attempt to
simply copy the text form field in the word doc I can't get the form to
select more than one of the options. It simple changes the first selected
text form field. I was under the impression that I could have a user select
more than one of the drop down choices by holding the ctrl key, but
apparently that doesn’t work either. Since I'm this far I'm quite sure it's
a simple oversight within the Visual Basic properties or code. Any help is
appreciated!
 
J

Jay Freedman

Hi Jim,

Wow, you're a quick study!

You're correct, the code given in the KB article
(http://support.microsoft.com/?kbid=306258) is written for a single-selection
list box in the userform, which transfers that single value to a single text box
in the document. That happens in the ComboBox1_Change() procedure, which fires
each time the user clicks a different item in the list.

Let's modify the userform in several ways. First, instead of placing a ComboBox
on the userform (if you followed the instructions exactly), place a ListBox
there. While the ListBox is selected on the design surface, go to the Properties
pane (if it isn't visible, press F4) and change these two properties:

ListStyle = 1 - fmListStyleOption
MultiSelect = 1 - fmMultiSelectMulti

These make the list box show check boxes next to the items, and allow multiple
selections by clicking on the items.

Remove the ComboBox1_Change() procedure, and instead insert this procedure:

Private Sub cmdClose_Click()
Dim rslt As String
Dim inx As Long

With ListBox1
For inx = 0 To .ListCount - 1
If .Selected(inx) Then
rslt = rslt & .List(inx) & " "
End If
Next
End With

ActiveDocument.FormFields("Text1").result = Trim(rslt)

Unload Me
End Sub

Short course:

- The "With ... End With" pair says that anything in between them that starts
with a dot refers to a property of the object named in the With statement. In
this case, .Selected(inx) is equivalent to the expression
ListBox1.Selected(inx), and similarly for .List(inx).

- The .Selected(inx) method returns True if the list item having the index value
inx was selected by the user. The For ... Next loop looks at each list item in
turn and, if it's selected, adds that item to the end of the string rslt,
separating items with a space. The & symbol is the string concatenation
operator.

- After all the items have been processed, the Trim function removes the
unneeded blank after the last item, and the remainder if the string is placed in
the form field.

If you plan to distribute this widely, it needs a few other things. One is error
trapping -- for example, checking for the existence of the named form field, and
maybe checking that at least one item was selected. Also, if the field might be
revised after it's already been filled once, you would want a mechanism to
"read" the form field and select the corresponding items in the list box before
the userform appears.

--
Regards,
Jay Freedman
Microsoft Word MVP
Email cannot be acknowledged; please post all follow-ups to the newsgroup so all
may benefit.
 
J

Jim G

Thanks Jay!

I'm almost there and do greatly appreciate the help. The form looks clean
and professional and, but I 'm looking for a minor change. Instead of the
items being seperated by a space with the trim function removing the last
space, I'd rather see a forced enter where the item is placed on a seperate
line instead of a long run. I've tried "playing" with the code, but I simply
can't get it to do anything more than add spacing or text to seperate. I'm
going to keep browsing for the answer, but wanted you to know you've been a
great help.
 
J

Jay Freedman

Hi Jim,

The first fact you need is that the built-in constant vbCr represents a
"carriage return", which Word interprets as a paragraph mark. You can add
this to the end of the string instead of the space character, by writing

rslt = rslt & .List(inx) & vbCr

The second thing, as a result of this, is that you need to remove the last
vbCr from the final string, but you can't do that with the Trim function
(which removes only spaces). So replace the statement that fills the form
field, with this:

If Right(rslt, 1) = vbCr Then
rslt = Left(rslt, Len(rslt) - 1)
End If
ActiveDocument.FormFields("Text1").Result = rslt

It says "if the one character at the right end of rslt is a vbCr, then take
the left part of rslt up to, but not including, the last character".
 
J

Jim G

Jay,

You've been an unbelievable help! I've created several user forms and
modules for the various needs on my form, but I'm running into two issues I
think I need to rectify before I distribute.

1. Today I received the Run-Time Error '4609" String too long. My
selections are sentences so I am assuming it's simply too much text. I found
a work around, but I can get it work and the Microsoft support page does not
state where to insert the work around.

2. My form has several simple controls before the macro (i.e. date, rich
text for name, etc.). When the .docm is locked and ready to test/distribute
and re-opened, the text form field assigned to the first user form
immediately appears on the screen before the user tabs down to that section.
I realize its esthetics so I'm going to play with the properties of the form.
I'm hoping it’s in that section.

Again, thanks for your help!!
 
J

Jay Freedman

Hi Jim,

The "string too long" error is covered in
http://www.word.mvps.org/FAQs/MacrosVBA/SetLongFmFldResult.htm. For your
userform code, you would replace the line

ActiveDocument.FormFields("Text1").Result = rslt

with

ActiveDocument.Unprotect
ActiveDocument.Bookmarks("Text1").Range.Fields(1).Result.Text = rslt
ActiveDocument.Protect Type:=wdAllowOnlyFormFields, NoReset:=True


For the other problem, do I understand correctly that you have both content
controls and legacy form fields in the same form? If so, do you have an
actual section break between the parts you're calling "sections"? If not,
put one there. Then, when you open the Protect Document pane, look for a
blue link "Select sections" below the dropdown where you chose "Filling in
forms". Click that link and, in the dialog, uncheck Section 1 to remove
forms protection for that section.

--
Regards,
Jay Freedman
Microsoft Word MVP
Email cannot be acknowledged; please post all follow-ups to the newsgroup so
all may benefit.
 
J

Jim G

Jay,

Fine tuning. I'd like to create a fail safe. For example, a user selects 5
items from the list group. Tabs to the next field and realizes he/she wants
to add a 6th item to the first user form. Can I change a setting or add code
so the user form remembers the 5 pre-selected items earlier instead of
starting over?

Thanks!
 
J

Jay Freedman

Hi Jim,

If you dig back far enough in this thread, you'll find that I did say "Also,
if the field might be revised after it's already been filled once, you would
want a mechanism to "read" the form field and select the corresponding items
in the list box before the userform appears." :)

This isn't too difficult -- essentially, the code of the Userform_Initialize
procedure has to take the current contents of the field (if any), break it
into its items, and select each item where it appears in the list box on the
userform.

But wait! What if the user has edited some or all of the items in the field
so they don't match the items in the list box? The code won't be able to
select anything for them. The solution is to store a copy of the field
contents where the user can't make a mess of it. That place is a "document
variable", a string that's stored invisibly and accessible only to macro
code, and that's stored in the document file during each save. This makes
the macro code more complex, but with the benefit of predictable behavior.

Here are the modified cmdClose_Click and Userform_Initialize procedures. (Of
course, you'll have to fold in your process for loading the list box's
..List, and change the names of fields, buttons, etc. as necessary.) You can
give any value (not containing spaces or punctuation, and I think up to 32
characters) to the constant myVarName.

Const myVarName = "JGitems"

Private Sub cmdClose_Click()
Dim rslt As String
Dim inx As Long

With ListBox1
For inx = 0 To .ListCount - 1
If .Selected(inx) Then
rslt = rslt & .List(inx) & vbCr
End If
Next
End With

If Right(rslt, 1) = vbCr Then
rslt = Left(rslt, Len(rslt) - 1)
End If

With ActiveDocument
.Variables(myVarName).Value = rslt ' <== new
.Unprotect
.Bookmarks("Text1").Range.Fields(1).Result.Text = rslt
.Protect Type:=wdAllowOnlyFormFields, NoReset:=True
End With

Unload Me
End Sub

Private Sub UserForm_Initialize()
Dim myVar As Variable
Dim myList As Variant
Dim myItem As Variant
Dim inx As Long

' change this to use your data
ListBox1.List = Array("one", "two", "three", "four", "five", "six")

With ActiveDocument ' <== new from here to End Sub
For Each myVar In .Variables
If myVar.Name = myVarName Then
myList = Split(myVar.Value, vbCr)
For Each myItem In myList
For inx = 0 To ListBox1.ListCount - 1
If ListBox1.List(inx) = myItem Then
ListBox1.Selected(inx) = True
Exit For
End If
Next inx
Next myItem
Exit For
End If
Next myVar
End With
End Sub

The new line in cmdClose_Click just assigns the value of rslt to the
document variable (and creates the variable if it doesn't already exist).

The new code in UserForm_Initialize first loops through the existing
document variables (if any) until it finds one whose name matches myVarName.
(If there are no variables, or if none have that name, the loop does
nothing.) This loop is necessary because trying to access a nonexistent
document variable causes an error. Although this could be handled by an On
Error statement, I prefer not to use the "I expected this error" method of
programming when it's easily avoided.

If the loop finds the right variable, it uses the Split function to make a
"variant array" containing the separate items within the variable's value.
The splits occur at the vbCr characters.

The next thing is to loop through all the separate items in the variant
array. For each one, the "For inx" loop finds the corresponding item in the
list box's .List and makes it selected.

--
Regards,
Jay Freedman
Microsoft Word MVP
Email cannot be acknowledged; please post all follow-ups to the newsgroup so
all may benefit.
 
J

Jim G

Thanks Jay! Just got back into this form and everything works well. A few
bugs hear and there, but I'm pleased. Appreciate your help.
 

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