Output Text to Document based on InputField Value - vba

I have this InputBox ("numbers") in my VBA form where I want to enter a value (usually between 1 and 10).
Based on the number I entered I want it to add the word "TOP" and a value to my Word document where I placed a bookmark ("t_1"), e.g. when I enter "3" I want it to output
TOP 1
TOP 2
TOP 3
This is what I am currently using but I am getting an error for my Range..
Private Sub
Dim tops As Integer
With ActiveDocument
.Bookmarks("t_1").Select
For Each tops In Range(Numbers)
Selection.TypeText ("TOP" & tops)
Next tops
End With
End Sub

This should work:
Private Sub ProduceHeadings()
' Charles Kenyon
' https://stackoverflow.com/questions/69723303/output-text-to-document-based-on-inputfield-value
Dim iTops As Long, i As Long
On Error GoTo NoBookmarks
ActiveDocument.Bookmarks("t_1").Select
iTops = InputBox("Number of Headings? (Integer)?", "Number of Headings")
For i = 1 To iTops
Selection.TypeText ("TOP " & i)
If i < iTops Then Selection.TypeText(vbCr)
Next i
On Error GoTo -1
Exit Sub
NoBookmarks:
On Error GoTo -1
MsgBox "The bookmark t_1 cannot be found in " & ActiveDocument.Name & "!", Buttons:=vbCritical
End Sub
Your bookmark should already have a bullet if you want these to be bullet points.
Here is a temporary link to a sample document with the macro.
https://www.dropbox.com/s/pp17heakgc9pd8b/deleteme%20add%20bullets.docm?dl=0
Far simpler, IMO, would be to use AutoText with a SEQ Field.
Simply have a Sequence field named something link myHead.
{ SEQ myHead }
Create and save AutoText entries for each of the setups you want using the sequence field:
So your AutoText named _top1 would have:
TOP { SEQ myHead }
Your AutoText named _top3 would have:
TOP { SEQ myHead }
TOP { SEQ myHead }
TOP { SEQ myHead }
If you later wanted to add another in your document, you would use the _top1 to add another.
Here is my writing on AutoText. http://www.addbalance.com/usersguide/autotextautocorrect.htm
Here is my writing on the Sequence Field. http://www.addbalance.com/usersguide/numbering.htm#Sequence_Fields
Here is my article on dealing with Fields on the Microsoft website. https://answers.microsoft.com/en-us/msoffice/forum/all/dealing-with-fields-in-microsoft-word/e1bdddad-5a51-4327-837c-c3786c16a501

Related

MS Word VBA Macro from validation

I'm trying to do what, I think, ought to be the simplest of things, but I can't get it to work. i have an MS Word document with a number of legacy drop-down and text fields:
The first option in each drop-down is "Select ...", and if a user tabs out of one of the drop-downs without choosing something other than the first "Select ...", I want a msgbox to appear to tell them to make a selection, which works. What doesn't work, is that after the user dismisses the msgbox, I want the insertion point to return to the drop-down that they didn't select.
I understand that VBA has "timing issues", and from what I've read, one way to address these timing issues is to call the "return" macro from the validation macro. So I've written two macros:
Sub Validate()
' Dim strBookmark As String
' strBookmark = Selection.Bookmarks(1).Name
10: If (bool_debug) Then MsgBox ("NotSelected() - 10: strBookmark = " & strBookmark)
With ActiveDocument
If (strBookmark = "Locality") Then
Call Salary_Step
ElseIf (strBookmark = "Series") Then
20: If (bool_debug) Then MsgBox ("NotSelected() - 20: .FormFields(strBookmark).Name = " _
& .FormFields(strBookmark).Name)
If ((Len(.FormFields(strBookmark).Result) <> 4) Or (Not IsNumeric(.FormFields(strBookmark).Result))) Then _
MsgBox ("Please enter a 4 digit number.")
Call GoBackToPrevious(.FormFields(strBookmark).Name)
ElseIf (.FormFields(strBookmark).DropDown.Value = 1) Then
MsgBox ("Please select a " & Replace(Selection.FormFields(strBookmark).Name, "_", " ") & ".")
Call GoBackToPrevious(.FormFields(strBookmark).Name)
End If
End With
End Sub
and
Sub GoBackToPrevious(strPreviousField)
10: If (bool_debug) Then MsgBox ("GoBacktoPrevious - 10: strPreviousField = " & strPreviousField)
ActiveDocument.Bookmarks(strPreviousField).Range.Fields(1).Result.Select
End Sub
But when I tab out of any of the form fields, the insertion point jumps to the next form field and not back to the one that I just tabbed out of.
I know from the debug code that GoBackToPrevious is being passed the name of the current form field, but MS Word advances to the next field regardless.
I'd really appreciate it if someone can tell me how make MS Word return to and select the drop-down the user did not select appropriately instead of jumping to and selecting the next form field in the document.
Thank you.
P James Norris
EDIT:
Based on #TimothyRylatt comments, I have modified my macro and when they're called.
I have edited Validate as above (commenting out the Dim the strBookmark assignment, and I call it "on entry" to the next form field.
strBookmark is Dimed on the module's declaration section:
Option Explicit
Const bool_debug As Boolean = True
Const str_password As String = "###" ' I have a different password
Public strBookmark As String
and "on exit" from the "current" form field, I attempt to store the "current" bookmark name:
Sub StoreBookmark()
strBookmark = Selection.Bookmarks(1).Name
10: If (bool_debug) Then MsgBox ("StoreBookmark() - 10: strBookmark = " & strBookmark)
End Sub
which I call from the current form field "on exit".
But when I tab out of the current form field to the next form field, the insertion point doesn't go back to the "current" but instead stays in the next form field.
Anyone have any other suggestions/insights?
Thanks,
P James Norris

Problems with MS Word DocVariables defined in VBA

Background: a proprietary piece of veterinary software generates a document pre-populated with merge fields containing data for a particular patient.
The field I am interested in is weight but its a string (Top_Stat) that looks like this "24.5 kg".
I have created a script to read that field and convert it into an integer. However I now want to use this integer to male medication dose calculations based on the animal weight.
As create document variables for this but the variable (name and value) gets stored in the document. I want at least the value to be removed but can't seem to get the result with the following script.
Sub GetWeight()
ActiveDocument.Variables("WeightInKg").Delete
WeightInt = ActiveDocument.MailMerge.DataSource.DataFields("Top_Stat").Value
WeightInt = Replace(WeightInt, " kg", "") 'This removes the superfluous text
WeightInt = Val(WeightInt) 'This converts the weight into a number (integer)
ActiveDocument.Variables.Add Name:="WeightInKg", Value:=WeightInt 'Add the Word variable
ActiveDocument.Fields.Update
End Sub
What am I missing? Apologies, I am new to VBA.
Your code needs some error checking. This first time it is run the document variable "WeightInKg" does not exist and when you go to delete it, the routine errors out.
Document variables, not to be confused with VBA Subroutine variables are not Word document fields so unless you have another reason for updating all fields, that code line is unnecessary.
Finally, you should always declare your VBA Subroutine variables.
I have modified your code but could not fully test it because I don't have your mail merge data source ... but give it a try and see if it now works for you.
Sub AutoOpen()
Call GetWeight
End Sub
Sub GetWeight()
Dim WeightIn As Long
On Error Resume Next
ActiveDocument.Variables("WeightInKg").Delete
On Error GoTo ErrHandler
WeightInt = ActiveDocument.MailMerge.DataSource.DataFields("Top_Stat").Value
WeightInt = Replace(WeightInt, " kg", "") 'This removes the superfluous text
WeightInt = Val(WeightInt) 'This converts the weight into a number (integer)
ActiveDocument.Variables.Add Name:="WeightInKg", Value:=WeightInt 'Add the Word variable
' ActiveDocument.Fields.Update
ErrHandler:
If Err.Number > 0 Then
MsgBox Err.Number & vbCr & Err.Description, vbCritical
Err.Clear
End If
End Sub
This is the screenshot of the Word document I am trying to populate.
Screenshot

VBA Word Insert text dynamically - Problems with ContentControl - Alternatives?

I am struggling quite a bit with ContentControl(s) in my Word VBA project.
There are a number of content control text fields which all have the same name (they have the same name because at the beginning the total number of required fields is not known, so I copy and paste the fields as many times as required). Now I want to loop through the content control fields and change the name of the fields based on the index of the individual items (e.g. first field in the document = "One", second field in the document = "two" and so on).
However, as mentioned in other threads, the index of the content control element does not correspond to its position in the document (I do not know, what it corresponds to).
Thus, instead of getting the fields in order, I get e.g. "four" --> "one" --> "three" --> "two" (or any other possible combination).
The content of the fields is coming from UserForm TextBoxes. The text boxes are named Text_Box_1 to Text_Box_4:
Private Sub Test() 'Note: The actual code is more complex, this is just to demonstrate my problem.
Dim i As Integer
UserForm1.TextBox1 = "one"
UserForm1.TextBox2 = "two"
UserForm1.TextBox3 = "three"
UserForm1.TextBox4 = "four"
For i = 1 To 4 - 1 'Since there are four text boxes in the UserForm in this example, the text snippet containing the text field gets copied and pasted three times; Note: Here the number of textboxes is pre-determined and fixed, in the actual project, it is variable.
ActiveDocument.Bookmarks(Index:="Copy").Range.Copy '
ActiveDocument.Bookmarks(Index:="Paste").Range.Paste
Next i
For i = 1 To 4 'This code is supposed to loop through the four content control text fields and insert text from the corresponding UserForm text box. However, content control text field 1, unfortunately does no correspond to UserForm.TextBox1 for some reason.
ActiveDocument.SelectContentControlsByTitle("Number").Item(i).Range.Text = UserForm1.Controls("TextBox" & i)
Next i
End Sub
Before running the code
After runnning the code
Is there any way to name to content control fields in the right order?
If not, what would be an alternative method to achieve my goals.
I think legacy text fields are not an option, since the document has to be protected; I have not looked into ActiveX text fields too much; Text boxes (shapes) might be another option, but they might have their own drawbacks.
It is really frustrating that the content control fields are behaving so weirdly and that something seemingly very simple and straight-forward can be so complicated (at least for me).
edit: Fixed a typo in the title.
Rather than use copy and paste I would insert the required text and content controls in my routine, something like this.
Private Sub Test()
Dim i As Integer
UserForm1.TextBox1 = "one"
UserForm1.TextBox2 = "two"
UserForm1.TextBox3 = "three"
UserForm1.TextBox4 = "four"
Dim cc As ContentControl
Dim rng As Range
Dim ccLocation As Range
For i = 4 To 1 Step -1 'Insert in reverse order to ensure that they are correct in the document
Set rng = ActiveDocument.Bookmarks("Paste").Range
rng.InsertAfter Text:="Number: "
rng.Collapse wdCollapseEnd
Set ccLocation = rng.Duplicate
rng.InsertAfter vbCr & "----------------------------------------" & vbCr
Set cc = ccLocation.ContentControls.Add(wdContentControlText)
cc.Range.Text = UserForm1.Controls("TextBox" & i).Text
cc.Title = "Number" & i
Next i
End Sub
If you cannot delete the existing content and must work with what you have then you could use the following:
Private Sub Test()
Dim i As Integer
UserForm1.TextBox1 = "one"
UserForm1.TextBox2 = "two"
UserForm1.TextBox3 = "three"
UserForm1.TextBox4 = "four"
ActiveDocument.SelectContentControlsByTitle("Number").Item(i).Range.Text = UserForm1.Controls("TextBox1").Text
Dim cc As ContentControl
Dim rng As Range
Dim ccLocation As Range
For i = 4 To 2 Step -1 'Insert in reverse order to ensure that they are correct in the document
Set rng = ActiveDocument.Bookmarks("Paste").Range
rng.InsertAfter Text:="Number: "
rng.Collapse wdCollapseEnd
Set ccLocation = rng.Duplicate
rng.InsertAfter vbCr & "----------------------------------------" & vbCr
Set cc = ccLocation.ContentControls.Add(wdContentControlText)
cc.Range.Text = UserForm1.Controls("TextBox" & i).Text
cc.Title = "Number" & i
Next i
End Sub

Check for duplicates in a text form field

I am a VBA noob and I'm trying to compare the numbers entered in a text form field in ms word, then if a duplicate is found I display a msg box.
The problem I'm having is... I have to compare 33 fields and look for a duplicate of anywhere from 1 to 33. I'm receiving a message that the procedure is too large. I'm sure there must be an easier way to do this. It's a protected document which I unprotect once the user hits the command button. I've even tried breaking it up and assigning macros to some of the fields.
Here is a sample of what I have. There are 33 bookmarks and I'm comparing each field.
If (ActiveDocument.FormFields("s1").Result = "1" And _
ActiveDocument.FormFields("s2").Result = "1") Then
MsgBox ("Your preferences cannot be duplicated.")
Validate = True
If True Then Exit Sub
Else
Validate = False
End If
Perhaps:
Sub CompareFormfields()
Dim i As Long, j As Long
With ActiveDocument
For i = 1 To .FormFields.Count - 1
For j = i + 1 To .FormFields.Count
If (.FormFields(i).Type = wdFieldFormTextInput) And (.FormFields(i).Type = wdFieldFormTextInput) Then
If .FormFields(i).Result = .FormFields(j).Result Then
MsgBox "The data in FormField " & i & " is duplicated in FormField " & j
End If
End If
Next
Next
End With
MsgBox "Done checking"
End Sub

How to select part of a text field using ms word macros

I am trying to build template invoices for Xero. Xero looks for specific fields in your MS Word template and inputs the variable assigned to that text field name in your given format. In word you can toggle the field code to view as just the field name:
«InvoiceNumber»
or the name with format:
{ MERGEFIELD InvoiceNumber \* MERGEFORMAT}
This outputs: INV1234 successfully into the template. Now what I need to do is output just the last 4 characters.
This post seems to imply it must be done with a VBA. I put together a macro with Visual Basic in word and this is where I have hit trouble:
Sub InvoiceNumber()
Dim MyInv As FormFields
Set MyInv = ActiveDocument.FormFields
If MyInv("Text1").Result = "InvoiceNumber" Then
MyInv("Text1").CheckBox.Value = Right(MyInv("Text1"), 4)
End If
End Sub
This returns with
error 5941: The requested member of the selection does not exist
I am quite a beginner with VB macros in word, what am I doing wrong and how should I instead be trying to call the InvoiceNumber Field?
Please try with the following solution:
Sub InvoiceNumber()
Dim MyInv As Field
Set MyInv = GetFieldByName("InvoiceNumber")
If Not MyInv Is Nothing Then
'do something with field result...
'here... debug to Immediate window
Debug.Print Right(MyInv.Result, 4)
End If
End Sub
Function GetFieldByName(fName As String) As Field
Dim F As Field
For Each F In ActiveDocument.Fields
'if not working try with (1) istead of (2) in line below
If Split(Replace(F.Code, " ", " "), " ")(2) = fName Then
Set GetFieldByName = F
Exit Function
End If
Next F
Set GetFieldByName = Nothing
End Function