I have some code I want to run - but the bookmarks in Word keep getting deleted - I want it to "overwrite" the last Input not add it.
Dim NAME As Range
Set NAME = ActiveDocument.Bookmarks("Username").Range
NAME.Text = Me.TextName.Value
Dim Surname As Range
Set Surname = ActiveDocument.Bookmarks("UsernameSurname").Range
Surname.Text = Me.TextSurname.Value
Does anyone have an solution for this? Thanks.
Your code doesn't delete the bookmarks - it simply inserts content after them.
To update the bookmarks, you might use, for example:
Call UpdateBookmark("Username",Me.TextName.Value)
Call UpdateBookmark("UsernameSurname",Me.TextSurname.Value)
coupled with:
Sub UpdateBookmark(StrBkMk As String, StrTxt As String)
Dim BkMkRng As Range
With ActiveDocument
If .Bookmarks.Exists(StrBkMk) Then
Set BkMkRng = .Bookmarks(StrBkMk).Range
BkMkRng.Text = StrTxt
.Bookmarks.Add StrBkMk, BkMkRng
End If
End With
Set BkMkRng = Nothing
End Sub
Related
I am trying to generate multiple Word documents which have content controls that are populated from an Excel file. The second content control needs to be populated with a list which varies in length.
How do I add each value to the content control instead of replacing the current value? I am currently using Rich Text Content Controls.
Here is what I have so far:
Sub CreateCoverLetters()
Dim objWord As Word.Application
Dim wDoc As Word.Document
Dim Rows As Integer
Set objWord = CreateObject(Class:="Word.Application")
objWord.Visible = True
Set wDoc = objWord.Documents.Open(*insert filepath*)
objWord.Activate
wDoc.ContentControls(1).Range.Text = Worksheets("Lists").Range("A2").Value
Rows = Worksheets("Lists").Range("A3", Range("A3").End(xlDown)).Rows.Count
r = 3
For i = 1 To Rows
wDoc.ContentControls(2).Range.Text = Worksheets("Lists").Cells(r, 1).Value
r = r + 1
Next
wDoc.SaveAs (*insert filepath*)
End Sub
Any help much appreciated!
Solved it as follows:
Sub CreateCoverLetters()
Dim objWord As Word.Application
Dim wDoc As Word.Document
Dim Rows As Integer
Dim Content As String
Set objWord = CreateObject(Class:="Word.Application")
objWord.Visible = True
Set wDoc = objWord.Documents.Open(*insert filepath*)
objWord.Activate
wDoc.ContentControls(1).Range.Text = Worksheets("Lists").Range("A2").Value
Rows = Worksheets("Lists").Range("A3", Range("A3").End(xlDown)).Rows.Count
r = 3
For i = 1 To Rows
Content = Content & "- " & Worksheets("Lists").Cells(r, 1).Value & vbNewLine
r = r + 1
Next
wDoc.ContentControls(2).Range.Text = Content
wDoc.SaveAs (*insert filepath*)
End Sub
The approach in user's answer works if the content can 1) be concatenated in a single string and 2) none of the elements require special formatting. This would also be the fastest approach.
If for any reason this process is not possible, then the way to "append" content without replacing goes something like in the code snippet that follows.
Notice how Range and ContentControl objects are declared and instantiated, especially the Range object. This makes it much easier to pick up the "target" at a later point in the code. Also, a Range object can be collapsed (think of it like pressing the right-arrow to make a selection a blinking cursor): this makes it possible to append content and work with that new content (format it, for example). Word also has a Range.InsertAfter method which can be used if the new content does not have to be manipulated in any special way.
Dim cc as Object ' Word.ContentControl
Dim rngCC as Object 'Word.Range
Set cc = wDoc.ContentControls(1).Range
Set rngCC = cc.Range
rngCC.Text = Worksheets("Lists").Range("A2").Value
'Add something at a later point
rngCC.Collapse wdCollapseEnd
rngCC.Text = " New material at the end of the content control."
I have two Word documents that both contain one word in each document.
I have third document that needs to pull each word from the two documents and edit all hyperlinks in the document using the replace function.
The replace function works if I enter a string into the function but does not work if when trying to pull the two words from the documents
Public Sub Document_Open()
Dim x As Document
Set newSource = Application.Documents.Open("\\t1dc\Everyone\Ben\ns.docx", ReadOnly:=True, Visible:=False)
Set oldSource = Application.Documents.Open("\\t1dc\Everyone\Ben\os.docx", ReadOnly:=True, Visible:=False)
Dim newServer As Range
Set newServer = newSource.Content
'Test using message box
MsgBox newServer
Dim oldServer As Range
Set oldServer = oldSource.Content
'Test using message box
MsgBox oldServer
For Each h In ActiveDocument.Hyperlinks
h.Address = Replace(h.Address, oldServer.Text, newServer.Text)
MsgBox h.Address
Next
newSource.Close
oldSource.Close
Set x = Nothing
End Sub
Check the length of oldServer and newServer, because you're also pulling in the \r that is at the end of a line.
Yes thank you!
Removed from end of string and it works
Dim oldS As String
oldS = Left(oldServer.Text, Len(oldServer.Text) - 1)
I had previously been using some VBA to pass fields from Access into a Word document, until coming up against the 255 character limit. Assistance from this site has led me to now use Bookmarks instead of Form Fields.
I was originally filling many different fields on Word and in some instances using the same data from Access in two places on the Word document. I was achieving this by calling:
.FormFields("txtReasonforReward").Result = Me![Reason for Reward]
.FormFields("txtReasonforReward2").Result = Me![Reason for Reward]
As I now have a different way of filling the "Reason for Reward" box, to circumvent the character limit (code below), I'm not sure how to fill "txtReasonforReward2". I do have several instances where I've added a second field and stuck a 2 on the end... I'm not convinced this is the best way so if anybody can advise on how to achieve this with both FormFields and Bookmarks, I'd be really grateful.
Dim objWord As Object
Set objWord = CreateObject("Word.Application")
objWord.Visible = True
Set doc = objWord.Documents.Open(***path to file***, , True)
Dim rng As Word.Range
Dim x As String
With doc
.FormFields("txtFirstName").Result = Me![First Name]
.FormFields("txtLastName").Result = Me![Last Name]
`examples cut for clarity...
.FormFields("txtHRID").Result = Me![ID]
.FormFields("txtPeriod").Result = Me![Period]
If doc.ProtectionType <> wdNoProtection Then
doc.Unprotect
End If
Set rng = doc.Bookmarks("txtReasonforReward").Range
rng.MoveStart wdCharacter, -1
x = rng.Characters.First
rng.FormFields(1).Delete
rng.Text = x & Me![Reason for Reward]
doc.Protect wdAllowOnlyFormFields, True
.Visible = True
.Activate
End With
objWord.View.ReadingLayout = True
Building on the code in the question and the background question...
Word can duplicate the content of a bookmark using REF field codes. Since form fields also use bookmark identifiers, this will work with existing form fields as well as bookmarked content. REF fields can be inserted directly, if a person is familiar with doing so OR by inserting a cross-reference to the bookmark.
Referring to the work-around for inserting more than 255 characters, in this case it will be necessary to also place a bookmark around the range being inserted and to update the REF fields so that they mirror the bookmark content throughout the document. The modified section of code is below.
'Declarations to be added at the beginning of the procedure
Dim fld As Word.Field
Dim bkmName As String
'Name of form field, bookmark to be added and text in REF field code
bkmName = "txtReasonforReward"
'Existing code
If doc.ProtectionType <> wdNoProtection Then
doc.Unprotect
End If
Set rng = doc.Bookmarks(bkmName).Range
rng.MoveStart wdCharacter, -1
x = rng.Characters.First
rng.FormFields(1).Delete
rng.Text = x & Me![Reason for Reward]
' New code
'Leave that single character out of the range for the bookmark
rng.MoveStart wdCharacter, 1
'Bookmark the inserted content
doc.Bookmarks.Add bkmName, rng
'Update fields so that REF's pick up the bookmark content
For Each fld In doc.Fields
If fld.Type = wdFieldRef Then
fld.Update
End If
Next
doc.Protect wdAllowOnlyFormFields, True
This approach will get a bit unwieldy if it needs to be applied to many fields. It might make sense to do something like write the bookmark names to a Tag property of the controls in the Access form then loop the controls to pick up bookmark name and data from the control, rather than writing each out explicitly - but this is just a thought for the future.
All that being said, the "modern" way to achieve this is to work with content controls instead of form fields/bookmarks. Content controls do not have the 255 character limit, the document can be protected as a form, multiple content controls can have the same title (name) and/or tag. Furthermore, content controls can be "mapped" to a Custom XML Part stored in the document so that changing the content of one will change the content of another. Trying to describe all that would go beyond what should be in an "answer", here, but is all publicly available by searching the Internet.
Personally, if this were my project and knowing what I know of it: If form fields are not required in the document (no user input via the fields is expected) I would use bookmarks and REF fields, only.
There are so may ways to do this kind of thing. Take a look at the approach below and see if you can get it to work.
Option Compare Database
' This concept uses Docvariables in MS Word
Sub PushToWord()
Dim wapp As Word.Application
Dim wdoc As Word.Document
Dim db As DAO.Database
Dim fld As DAO.Field
Dim rs As DAO.Recordset
Dim filenm As String
Dim NumFields As Integer
Dim i As Integer
Set db = CurrentDb
Set rs = db.OpenRecordset("tbl_CustomerData")
Set wapp = New Word.Application
wapp.Visible = True
Set rs = DBEngine(0)(0).OpenRecordset("SELECT * FROM tbl_CustomerData")
If rs.RecordCount > 0 Then
rs.MoveFirst
Do While Not rs.EOF
For i = 0 To rs.Fields.Count - 1
Set fld = rs.Fields(i)
Debug.Print fld.Name, fld.Value
If fld.Value = 20 Then
filenm = "C:\Users\Ryan\Desktop\Coding\Integrating Access and Word\From Access to Word\Letter1.doc"
Set wdoc = wapp.Documents.Open(filenm)
wapp.ActiveDocument.Variables("Name").Value = rs.Fields("Name").Value
End If
Next
rs.MoveNext
Loop
Set fld = Nothing
rs.Close
End If
Set rs = Nothing
End Sub
The code runs from Access, and you can fire it off any number of ways (button click event, form load event, some other object event, etc.)
I'm trying to access from Outlook VBA, either a variable or content control ID that I've created in a word Macro.
Basically I am trying to get set a text field equal to a string variable and load this variable to a message box in outlook.
From outlook, I have the code that creates a word object, and opens the active document, but I'm confused as to accessing the variables. I've tried making the variable in word VBA a public variable with no luck.
Current code to access the variable from outlook:
Set oWordApp = CreateObject("Word.Application")
Set oWordDoc = oWordApp.Documents.Open("C:\Owner\Desktop\Job.docx")
oWordApp.Visible = True
MsgBox(oWordApp.testtest)
Having a look at the ContentControl help file you can pull back the text from the content control using its Tag property.
Sub Test()
Dim oWordApp As Object
Dim oWordDoc As Object
Dim oContent As Variant
Dim oCC As Variant
Set oWordApp = CreateObject("Word.Application")
Set oWordDoc = oWordApp.Documents.Open("S:\DB_Development_DBC\Test\MyNewDoc.docm")
oWordApp.Visible = True
Set oContent = oWordDoc.SelectContentControlsByTag("MyCalendarTag")
If oContent.Count <> 0 Then
For Each oCC In oContent
MsgBox oCC.PlaceholderText & vbCr & oCC.Range.Text
Next oCC
End If
End Sub
The code above displayed Click here to enter a date. as the PlaceHolderText value and 01/01/2007 as the Range.Text value. So no need to add separate functions; just reference the content control directly.
https://msdn.microsoft.com/en-us/library/office/gg605189(v=office.14).aspx
https://msdn.microsoft.com/en-us/vba/word-vba/articles/working-with-content-controls
Edit
As an example of returning value from multiple controls in one function:
Public Sub Example()
Dim MySevenTags As Variant
Dim x As Long
MySevenTags = Array("Tag1", "Tag2", "Tag3", "Tag4", "Tag5", "Tag6", "Tag7")
For x = LBound(MySevenTags) To UBound(MySevenTags)
MsgBox ReturnFromWordContent(CStr(MySevenTags(x))), vbOKOnly
Next x
End Sub
Public Function ReturnFromWordContent(TagID As String) As Variant
Dim oWordApp As Object
Dim oWordDoc As Object
Dim oContent As Variant
Dim oCC As Variant
Set oWordApp = CreateObject("Word.Application")
Set oWordDoc = oWordApp.Documents.Open("S:\DB_Development_DBC\Test\MyNewDoc.docm")
oWordApp.Visible = True
Set oContent = oWordDoc.SelectContentControlsByTag(TagID)
'I've made this next bit up.
'No idea how to get the type of control, or how to return the values.
Select Case oContent.Type
Case "calendar"
ReturnFromWordContent = oContent.Range.Date
Case "textbox"
ReturnFromWordContent = oContent.Range.Text
Case Else
'Return some default value such as Null which
'won't work in this case as it's returning to a messagebox
'but you get the picture.
End Select
' If oContent.Count <> 0 Then
' For Each oCC In oContent
' MsgBox oCC.PlaceholderText & vbCr & oCC.Range.Text
' Next oCC
' End If
End Function
"I've tried making the variable in word VBA a public variable with no luck."
Declare your macro "testtest" as a function with the return value of your variable.
Public Function testtest() As String
dim myVariabel as String
myVariable = "test"
' return value
testtest = myVariable
End Function
Best regards
I am creating an Excel VBA script to send reports via email. I made the following function to validate an attachment before including it on the email.
The function below will open the word document, check if the first line matches a customer ID and return a bool.
It works, however, when I read the data from Word, it includes some hidden quotes into the text.
While both strings are 123a, when I paste them into another text editor, I see the one i read from Word as "123a". If i print them using MsbBox, both are equal to 123a.
Function ValidateAttachment(attachmentURL As String, customerID As String) As Boolean
Dim oWord As Word.Application
Dim oWdoc As Word.Document
Set oWord = CreateObject("Word.Application")
Set oWdoc = oWord.Documents.Open(attachmentURL)
If StrComp(oWdoc.Paragraphs(1).Range.Text, customerID, vbTextCompare) = 0 Then
ValidateAttachment = True
Else
ValidateAttachment = False
End If
oWord.Quit
Set oWord = Nothing
Exit Function
End Function
This is what i see when i write both results into regular cells. Even I i make a simple formula IF to check for equality, it doesn't work.
Try:
If StrComp(Replace(oWdoc.Paragraphs(1).Range.Text,chr(34),""), customerID, vbTextCompare) = 0 Then
I found how to deal with the invisible quotes.
Using this did the trick:
Application.WorksheetFunction.Clean()
And here is the final code:
Function ValidateAttachment(attachmentURL As String, customerID As String) As Boolean
Dim oWord As Word.Application
Dim oWdoc As Word.Document
Set oWord = CreateObject("Word.Application")
Set oWdoc = oWord.Documents.Open(attachmentURL)
Dim x As String
x = "123a"
Application.Sheets(1).Columns(3).Rows(2) = Replace(oWdoc.Paragraphs(1).Range.Text, Chr(34), "")
If StrComp(Application.WorksheetFunction.Clean(oWdoc.Paragraphs(1).Range.Text), customerID, vbTextCompare) = 0 Then
ValidateAttachment = True
Else
ValidateAttachment = False
End If
oWord.Quit
Set oWord = Nothing
Exit Function
End Function