I regularly have to create documents at work and within the company we almost have a language of our own due to the number of acronyms and abbreviations we use. Consequently I got tired of manually creating an Acronym and abbreviation table before I could publish the document and a quick google search came across a macro that would effectively do it for me. (modified code shown below)
I modified this macro so that the table was pasted into the location of the cursor in the original document (this may not be the msot efficient way, but it was the simplest i could think of as I am not a VBA expert).
Since then I have realised that there must be a simple way to further speed up this process by automatically including the definitions as well. I have an excel spreadsheet with the Acronym in the first column and its definition in the second.
So far I have been able to get as far as opening the excel document but cannot seem to get a search which will return the row number and consequently use this to copy the contents of the definition cell next to it into the corresponding definition section of the table in Word.
** edit - extra explanation **
The current macro searches the word document and finds all the acronyms that have been used and places them in a table in a seperate word document. What i wish to do is have it also then search an excel file (pre-existing) for the definition of each of the found acronyms and add them also to the table or if they are new leave it blank. Finally the macro copies this table back into the original document.
This code currently fails saying the .Find function is not defined? (I have kept the code seperate for now to keep testing simple)
Dim objExcel As Object
Dim objWbk As Object
Dim objDoc As Document
Dim rngSearch As Range
Dim rngFound As Range
Set objDoc = ActiveDocument
Set objExcel = CreateObject("Excel.Application")
Set objWbk = objExcel.Workbooks.Open("P:\ENGINEERING\EL\Global Access\Abbreviations and Acronyms.xls")
objExcel.Visible = True
objWbk.Activate
With objExcel
With objWbk
Set rngSearch = objWbk.Range("A:A")
Set rngFound = rngSearch.Find(What:="AS345", LookIn:=xlValues, LookAt:=xlPart)
If rngFound Is Nothing Then
MsgBox "Not found"
Else
MsgBox rngFound.Row
End If
End With
End With
Err_Exit:
'clean up
Set BMRange = Nothing
Set objWbk = Nothing
objExcel.Visible = True
Set objExcel = Nothing
Set objDoc = Nothing
'MsgBox "The document has been updated"
Err_Handle:
If Err.Number = 429 Then 'excel not running; launch Excel
Set objExcel = CreateObject("Excel.Application")
Resume Next
ElseIf Err.Number <> 0 Then
MsgBox "Error " & Err.Number & ": " & Err.Description
Resume Err_Exit
End If
End Sub
Acronym extraction code
Sub ExtractACRONYMSToNewDocument()
'=========================
'Macro created 2008 by Lene Fredborg, DocTools - www.thedoctools.com
'THIS MACRO IS COPYRIGHT. YOU ARE WELCOME TO USE THE MACRO BUT YOU MUST KEEP THE LINE ABOVE.
'YOU ARE NOT ALLOWED TO PUBLISH THE MACRO AS YOUR OWN, IN WHOLE OR IN PART.
'=========================
'Modified in 2014 by David Mason to place the acronym table in the original document
'=========================
Dim oDoc_Source As Document
Dim oDoc_Target As Document
Dim strListSep As String
Dim strAcronym As String
Dim strDef As String
Dim oTable As Table
Dim oRange As Range
Dim n As Long
Dim strAllFound As String
Dim Title As String
Dim Msg As String
Title = "Extract Acronyms to New Document"
'Show msg - stop if user does not click Yes
Msg = "This macro finds all words consisting of 3 or more " & _
"uppercase letters and extracts the words to a table " & _
"in a new document where you can add definitions." & vbCr & vbCr & _
"Do you want to continue?"
If MsgBox(Msg, vbYesNo + vbQuestion, Title) <> vbYes Then
Exit Sub
End If
Application.ScreenUpdating = False
'Find the list separator from international settings
'May be a comma or semicolon depending on the country
strListSep = Application.International(wdListSeparator)
'Start a string to be used for storing names of acronyms found
strAllFound = "#"
Set oDoc_Source = ActiveDocument
'Create new document for acronyms
Set oDoc_Target = Documents.Add
With oDoc_Target
'Make sure document is empty
.Range = ""
'Insert info in header - change date format as you wish
'.PageSetup.TopMargin = CentimetersToPoints(3)
'.Sections(1).Headers(wdHeaderFooterPrimary).Range.Text = _
' "Acronyms extracted from: " & oDoc_Source.FullName & vbCr & _
' "Created by: " & Application.UserName & vbCr & _
' "Creation date: " & Format(Date, "MMMM d, yyyy")
'Adjust the Normal style and Header style
With .Styles(wdStyleNormal)
.Font.Name = "Arial"
.Font.Size = 10
.ParagraphFormat.LeftIndent = 0
.ParagraphFormat.SpaceAfter = 6
End With
With .Styles(wdStyleHeader)
.Font.Size = 8
.ParagraphFormat.SpaceAfter = 0
End With
'Insert a table with room for acronym and definition
Set oTable = .Tables.Add(Range:=.Range, NumRows:=2, NumColumns:=2)
With oTable
'Format the table a bit
'Insert headings
.Range.Style = wdStyleNormal
.AllowAutoFit = False
.Cell(1, 1).Range.Text = "Acronym"
.Cell(1, 2).Range.Text = "Definition"
'.Cell(1, 3).Range.Text = "Page"
'Set row as heading row
.Rows(1).HeadingFormat = True
.Rows(1).Range.Font.Bold = True
.PreferredWidthType = wdPreferredWidthPercent
.Columns(1).PreferredWidth = 20
.Columns(2).PreferredWidth = 70
'.Columns(3).PreferredWidth = 10
End With
End With
With oDoc_Source
Set oRange = .Range
n = 1 'used to count below
With oRange.Find
'Use wildcard search to find strings consisting of 3 or more uppercase letters
'Set the search conditions
'NOTE: If you want to find acronyms with e.g. 2 or more letters,
'change 3 to 2 in the line below
.Text = "<[A-Z]{3" & strListSep & "}>"
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchCase = True
.MatchWildcards = True
'Perform the search
Do While .Execute
'Continue while found
strAcronym = oRange
'Insert in target doc
'If strAcronym is already in strAllFound, do not add again
If InStr(1, strAllFound, "#" & strAcronym & "#") = 0 Then
'Add new row in table from second acronym
If n > 1 Then oTable.Rows.Add
'Was not found before
strAllFound = strAllFound & strAcronym & "#"
'Insert in column 1 in oTable
'Compensate for heading row
With oTable
.Cell(n + 1, 1).Range.Text = strAcronym
'Insert page number in column 3
'.Cell(n + 1, 3).Range.Text = oRange.Information(wdActiveEndPageNumber)
End With
n = n + 1
End If
Loop
End With
End With
'Sort the acronyms alphabetically - skip if only 1 found
If n > 2 Then
With Selection
.Sort ExcludeHeader:=True, FieldNumber:="Column 1", SortFieldType _
:=wdSortFieldAlphanumeric, SortOrder:=wdSortOrderAscending
'Go to start of document
.HomeKey (wdStory)
End With
End If
'Copy the whole table, switch to the source document and past
'in the table at the original selection location
Selection.WholeStory
Selection.Copy
oDoc_Source.Activate
Selection.Paste
'make the target document active and close it down without saving
oDoc_Target.Activate
ActiveDocument.Close SaveChanges:=wdDoNotSaveChanges
Application.ScreenUpdating = True
'If no acronyms found, show msg and close new document without saving
'Else keep open
If n = 1 Then
Msg = "No acronyms found."
oDoc_Target.Close SaveChanges:=wdDoNotSaveChanges
Else
Msg = "Finished extracting " & n - 1 & " acronymn(s) to a new document."
End If
MsgBox Msg, vbOKOnly, Title
'Clean up
Set oRange = Nothing
Set oDoc_Source = Nothing
Set oDoc_Target = Nothing
Set oTable = Nothing
End Sub
You are just missing the Worksheet Object.
Also With objExcel can be ommited since you already pass the Workbook Object to objWbk variable.
With objWbk.Sheets("NameOfYourSheet")
Set rngSearch = .Range(.Range("A1"), .Range("A" & .Rows.Count).End(xlUp))
Set rngFound = rngSearch.Find(What:="AS345", After:=.Range("A1"), LookAt:=xlWhole)
If rngFound Is Nothing Then
MsgBox "Not found"
Else
MsgBox rngFound.Row
End If
End With
In the above code, I assumed your Excel data have headers.
Edit1: Since you are Late Binding Excel, this should work:
With objWbk.Sheets("Sheet1")
Set rngSearch = .Range(.Range("A1"), .Range("A" & .Rows.Count).End(-4162))
Set rngFound = rngSearch.Find(What:="AS345", After:=.Range("A1"), LookAt:=1)
If rngFound Is Nothing Then
MsgBox "Not found"
Else
MsgBox rngFound.Row
End If
End With
Take note that we replaced xlUp with it's equivalent constant -4162 and xlWhole with 1.
To learn more about Early and Late Binding, check THIS out.
For additional information, you can also refer HERE.
Although it is dicussed in the link I provided, you might ask where do I get the constant?
Just open Excel or any other MS application you are binding then view Immediate Window - Ctrl+G
In the immediate window, type ? then the constant you want to get the numeric equivalent.
Example:
?xlUp
-4162
?xlWhole
1
?xlPart
2
Hope this somehow solves your problem.
So it would appear with some searching I found the solution to the problem. A big thank you to L42 who helped solve the problem regarding whether i was using Early or Late binding (I had no idea these were even different).
The remaining problem where the following error occured:
Compile Error: Named Argument not found
Was suprisingly easy to solve once I found the solution... you have to love hindsight. It turns out I had to define my two variables rngFound and rngSearch as objects. As soon as i made that change the code worked beautifully.
Here is the working code which I will then incorporate into my acronym macro. (will add the total code when complete)
Sub openExcel()
Dim objExcel As Object
Dim objWbk As Object
Dim objDoc As Document
Dim rngSearch As Object
Dim rngFound As Object
Dim targetCellValue
Set objDoc = ActiveDocument
Set objExcel = CreateObject("Excel.Application")
Set objWbk = objExcel.Workbooks.Open("C:\Users\DMASON2\Documents\Book1.xlsx")
objExcel.Visible = True
objWbk.Activate
With objWbk.Sheets("Sheet1")
Set rngSearch = .Range(.Range("A1"), .Range("A" & .Rows.Count).End(-4162))
Set rngFound = rngSearch.Find(What:="AA", After:=.Range("A1"), LookAt:=1)
If rngFound Is Nothing Then
MsgBox "Not found"
Else
'MsgBox rngFound.Row
targetCellValue = .Cells(rngFound.Row, 2).Value
MsgBox (targetCellValue)
End If
End With
Err_Exit:
'clean up
Set BMRange = Nothing
Set objWbk = Nothing
objExcel.Visible = True
Set objExcel = Nothing
Set objDoc = Nothing
'MsgBox "The document has been updated"
Err_Handle:
If Err.Number = 429 Then 'excel not running; launch Excel
Set objExcel = CreateObject("Excel.Application")
Resume Next
ElseIf Err.Number <> 0 Then
MsgBox "Error " & Err.Number & ": " & Err.Description
Resume Err_Exit
End If
End Sub
** edit, complete code for searching and finding the acronyms along with their definitions **
Sub ExtractACRONYMSToNewDocument()
Dim oDoc_Source As Document
Dim oDoc_Target As Document
Dim strListSep As String
Dim strAcronym As String
Dim strDef As String
Dim oTable As Table
Dim oRange As Range
Dim n As Long
Dim m As Long
m = 0
Dim strAllFound As String
Dim Title As String
Dim Msg As String
Dim objExcel As Object
Dim objWbk As Object
Dim rngSearch As Object
Dim rngFound As Object
Dim targetCellValue As String
' message box title
Title = "Extract Acronyms to New Document"
' Set message box message
Msg = "This macro finds all Acronyms (consisting of 2 or more " & _
"uppercase letters, Numbers or '/') and their associated definitions. It " & _
"then extracts the words to a table at the current location you have selected" & vbCr & vbCr & _
"Warning - Please make sure you check the table manually after!" & vbCr & vbCr & _
"Do you want to continue?"
' Display message box
If MsgBox(Msg, vbYesNo + vbQuestion, Title) <> vbYes Then
Exit Sub
End If
' Stop the screen from updating
Application.ScreenUpdating = False
'Find the list separator from international settings
'May be a comma or semicolon depending on the country
strListSep = Application.International(wdListSeparator)
'Start a string to be used for storing names of acronyms found
strAllFound = "#"
' give the active document a variable
Set oDoc_Source = ActiveDocument
'Crete a variable for excel and open the definition workbook
Set objExcel = CreateObject("Excel.Application")
Set objWbk = objExcel.Workbooks.Open("C:\Users\Dave\Documents\Test_Definitions.xlsx")
'objExcel.Visible = True
objWbk.Activate
'Create new document to temporarily store the acronyms
Set oDoc_Target = Documents.Add
' Use the target document
With oDoc_Target
'Make sure document is empty
.Range = ""
'Insert info in header - change date format as you wish
'.PageSetup.TopMargin = CentimetersToPoints(3)
'.Sections(1).Headers(wdHeaderFooterPrimary).Range.Text = _
' "Acronyms extracted from: " & oDoc_Source.FullName & vbCr & _
' "Created by: " & Application.UserName & vbCr & _
' "Creation date: " & Format(Date, "MMMM d, yyyy")
'Adjust the Normal style and Header style
With .Styles(wdStyleNormal)
.Font.Name = "Arial"
.Font.Size = 10
.ParagraphFormat.LeftIndent = 0
.ParagraphFormat.SpaceAfter = 6
End With
With .Styles(wdStyleHeader)
.Font.Size = 8
.ParagraphFormat.SpaceAfter = 0
End With
'Insert a table with room for acronym and definition
Set oTable = .Tables.Add(Range:=.Range, NumRows:=2, NumColumns:=2)
With oTable
'Format the table a bit
'Insert headings
.Range.Style = wdStyleNormal
.AllowAutoFit = False
.Cell(1, 1).Range.Text = "Acronym"
.Cell(1, 2).Range.Text = "Definition"
'Set row as heading row
.Rows(1).HeadingFormat = True
.Rows(1).Range.Font.Bold = True
.PreferredWidthType = wdPreferredWidthPercent
.Columns(1).PreferredWidth = 20
.Columns(2).PreferredWidth = 70
End With
End With
With oDoc_Source
Set oRange = .Range
n = 1 'used to count below
' within the total range of the source document
With oRange.Find
'Use wildcard search to find strings consisting of 3 or more uppercase letters
'Set the search conditions
'NOTE: If you want to find acronyms with e.g. 2 or more letters,
'change 3 to 2 in the line below
.Text = "<[A-Z][A-Z0-9/]{1" & strListSep & "}>"
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchCase = True
.MatchWildcards = True
'Perform the search
Do While .Execute
'Continue while found
strAcronym = oRange
'Insert in target doc
'If strAcronym is already in strAllFound, do not add again
If InStr(1, strAllFound, "#" & strAcronym & "#") = 0 Then
'Add new row in table from second acronym
If n > 1 Then oTable.Rows.Add
'Was not found before
strAllFound = strAllFound & strAcronym & "#"
'Insert in column 1 in oTable
'Compensate for heading row
With oTable
.Cell(n + 1, 1).Range.Text = strAcronym
' Find the definition from the Excel document
With objWbk.Sheets("Sheet1")
' Find the range of the cells with data in Excel doc
Set rngSearch = .Range(.Range("A1"), .Range("A" & .Rows.Count).End(-4162))
' Search in the found range for the
Set rngFound = rngSearch.Find(What:=strAcronym, After:=.Range("A1"), LookAt:=1)
' if nothing is found count the number of acronyms without definitions
If rngFound Is Nothing Then
m = m + 1
' Set the cell variable in the new table as blank
targetCellValue = ""
' If a definition is found enter it into the cell variable
Else
targetCellValue = .Cells(rngFound.Row, 2).Value
End If
End With
' enter the cell varibale into the definition cell
.Cell(n + 1, 2).Range.Text = targetCellValue
End With
' add one to the loop count
n = n + 1
End If
Loop
End With
End With
'Sort the acronyms alphabetically - skip if only 1 found
If n > 2 Then
With Selection
.Sort ExcludeHeader:=True, FieldNumber:="Column 1", SortFieldType _
:=wdSortFieldAlphanumeric, SortOrder:=wdSortOrderAscending
'Go to start of document
.HomeKey (wdStory)
End With
End If
'Copy the whole table, switch to the source document and past
'in the table at the original selection location
Selection.WholeStory
Selection.Copy
oDoc_Source.Activate
Selection.Paste
' update screen
Application.ScreenUpdating = True
'If no acronyms found set message saying so
If n = 1 Then
Msg = "No acronyms found."
' set the final messagebox message to show the number of acronyms found and those that did not have definitions
Else
Msg = "Finished extracting " & n - 1 & " acronymn(s) to a new document. Unable to find definitions for " & m & " acronyms."
End If
' Show the finished message box
AppActivate Application.Caption
MsgBox Msg, vbOKOnly, Title
'make the target document active and close it down without saving
oDoc_Target.Activate
ActiveDocument.Close SaveChanges:=wdDoNotSaveChanges
'Close Excel after
objWbk.Close Saved = True
'Clean up
Set oRange = Nothing
Set oDoc_Source = Nothing
Set oDoc_Target = Nothing
Set oTable = Nothing
Set objExcel = Nothing
Set objWbk = Nothing
End Sub
Related
I am trying to write a Macro that can display the final proposed text in a tracked change without having to accept the change.
Current code (modified from thedoctools.com) is as follows which uses a Revision object only for Delete and Insert types:
Public Sub ExtractAllRevisionsToExcel()
Dim oDoc As Document
Dim xlApp As Excel.Application
Dim xlWB As Excel.Workbook
Dim oNewExcel As Worksheet
Dim oRange As Range
Dim oRevision As Revision
Dim strText As String
Dim index As Long
Dim Title As String
Title = "Extract All revisions to Excel"
Set oDoc = ActiveDocument
If ActiveDocument.Revisions.Count = 0 Then
MsgBox "The active document contains no changes.", vbOKOnly, Title
GoTo ExitHere
End If
Application.ScreenUpdating = True
'Create a new excel for the revisions
Set xlApp = CreateObject("Excel.Application")
xlApp.Visible = True
Set xlWB = xlApp.Workbooks.Add 'create a new workbook
Set oNewExcel = xlWB.Worksheets(1)
With oNewExcel
.Cells(1, 1).Formula = "Document"
.Cells(1, 2).Formula = "Page"
.Cells(1, 3).Formula = "line number"
.Cells(1, 4).Formula = "Original Statement"
.Cells(1, 5).Formula = "Statement Proposed"
index = 1
'Get info from each tracked change (insertion/deletion) from oDoc and insert in table
For Each oRevision In oDoc.Revisions
Select Case oRevision.Type
'Only include insertions and deletions
Case wdRevisionInsert, wdRevisionDelete
'In case of footnote/endnote references (appear as Chr(2)),
'insert "[footnote reference]"/"[endnote reference]"
With oRevision
'Get the changed text
strText = .Range.Text
Set oRange = .Range
Do While InStr(1, oRange.Text, Chr(2)) > 0
'Find each Chr(2) in strText and replace by appropriate text
i = InStr(1, strText, Chr(2))
If oRange.Footnotes.Count = 1 Then
strText = Replace(Expression:=strText, _
Find:=Chr(2), Replace:="[footnote reference]", _
Start:=1, Count:=1)
'To keep track of replace, adjust oRange to start after i
oRange.Start = oRange.Start + i
ElseIf oRange.Endnotes.Count = 1 Then
strText = Replace(Expression:=strText, _
Find:=Chr(2), Replace:="[endnote reference]", _
Start:=1, Count:=1)
'To keep track of replace, adjust oRange to start after i
oRange.Start = oRange.Start + i
End If
Loop
End With
index = index + 1 'Add 1 to row
'Insert data in cells in row
'The document name
.Cells(index, 1).Formula = oDoc.FullName & vbCr
'Page number
.Cells(index, 2).Formula = oRevision.Range.Information(wdActiveEndPageNumber)
'Line number - start of revision
.Cells(index, 3).Formula = oRevision.Range.Information(wdFirstCharacterLineNumber)
'Original section text
.Cells(index, 4).Formula = oRevision.Range.Paragraphs(1).Range.Text
'Proposed changes - THIS IS WHERE I WANT TO SEE THE PREVIEW OF THE FINAL SECTION AFTER CHANGE IS ACCEPTED
If oRevision.Type = wdRevisionInsert Then
.Cells(index, 5).Formula = strText
'Apply automatic color (black on white)
.Cells(index, 5).Font.Color = wdColorBlue
Else
.Cells(index, 5).Formula = strText
'Apply red color
.Cells(index, 5).Font.Color = wdColorRed
End If
End Select
Next oRevision
End With
'Repaginate
ActiveDocument.Repaginate
'Toggle nonprinting characters twice
ActiveWindow.ActivePane.View.ShowAll = Not _
ActiveWindow.ActivePane.View.ShowAll
ActiveWindow.ActivePane.View.ShowAll = Not _
ActiveWindow.ActivePane.View.ShowAll
Application.ScreenUpdating = True
Application.ScreenRefresh
oNewExcel.Activate
MsgBox ActiveDocument.Revisions.Count & " changes found. Finished creating the worksheet.", vbOKOnly, Title
ExitHere:
Set oDoc = Nothing
Set xlWB = Nothing
Set xlApp = Nothing
Set oNewExcel = Nothing
Set oRange = Nothing
End Sub
The variable strText returns only the portion we are changing in oRevision.Range.Paragraphs(1).Range.Text, however I want a variable that returns the final text in oRevision.Range.Paragraphs(1).Range.Text AFTER the change has already been accepted, but without accepting the change in the actual Word document.
Is there a way to get such a variable as I just want to have a preview of the final section after the change is accepted, without accepting the change.
Even Word's macro recorder can give you the code for that:
With ActiveWindow.View
.ShowRevisionsAndComments = False
.RevisionsView = wdRevisionsViewFinal
End With
I am writing a vba macro for a word document. I use vba macro to generate textbox and text to the word document. The issue is that the textbox moves to the top of last page instead of staying on the first page.
I don't know what i am doing wrong. All i need is for that textbox to remain on the first page. I really need to include the textbox.
below is my code and the output image
Dim wrdDoc As Object
Dim tmpDoc As Object
Dim WDoc As String
Dim myDoc As String
myDoc = "myTest"
WDoc = ThisDocument.Path & "\mydocument.docx"
On Error Resume Next
Set wdApp = GetObject(, "Word.Application")
If wdApp Is Nothing Then
' no current word application
Set wdApp = CreateObject("Word.application")
Set wrdDoc = wdApp.Documents.Open(WDoc)
wdApp.Visible = True
Else
' word app running
For Each tmpDoc In wdApp.Documents
If StrComp(tmpDoc.FullName, WDoc, vbTextCompare) = 0 Then
' this is your doc
Set wrdDoc = tmpDoc
Exit For
End If
Next
If wrdDoc Is Nothing Then
' not open
Set wrdDoc = wdApp.Documents.Open(WDoc)
End If
End If
ActiveDocument.Content.Select
Selection.Delete
With wdApp
.Visible = True
.Activate
With .Selection
Dim objShape As Word.Shape
Set objShape2 = ActiveDocument.Shapes.addTextbox _
(Orientation:=msoTextOrientationHorizontal, _
Left:=400, Top:=100, Width:=250, Height:=60)
With objShape2
.RelativeHorizontalPosition = wdRelativeHorizontalPositionColumn
.RelativeVerticalPosition = wdRelativeVerticalPositionMargin
.Left = wdShapeRight
.Top = wdShapeTop
.TextFrame.TextRange = "This is nice and shine" & vbCrLf & "222"
.TextFrame.TextRange.ParagraphFormat.Alignment = wdAlignParagraphLeft
End With
End With
With .Selection
.TypeParagraph
.TypeParagraph
.TypeParagraph
.TypeParagraph
.TypeParagraph
.TypeParagraph
.TypeParagraph
For i = 1 To 40
.TypeText i
.TypeParagraph
Next i
End With
End With
Word Shape objects must be anchored to a character position in the Word document. They will always appear on the page where the anchor character is and, if the anchor formatting is not to the page, they will move relatively on the page with the anchor character.
A special case ensues when a document is "empty" (a lone paragraph), so it helps to make sure the document has more than one character in it. In the code sample below an additional paragraph is inserted before adding the TextBox - to the first paragraph.
I've made some other adjustments to the code:
Added On Error GoTo 0 so that error messages will appear. Otherwise, debugging becomes impossible.
Removed the With for the Word application since it's not necessary when using Word objects
Declared and use a Word Range object for inserting content. As with Excel, it's better to not work with Selection whenever possible.
Used the wrdDoc object you declare and instantiate instead of ActiveDocument.
This code worked fine in my test, but I cannot, of course, repro your entire environment.
Dim wrdDoc As Object
Dim tmpDoc As Object
Dim WDoc As String
Dim myDoc As String
myDoc = "myTest"
WDoc = ThisDocument.Path & "\mydocument.docx"
On Error Resume Next
Set wdApp = GetObject(, "Word.Application")
On Error GoTo 0
If wdApp Is Nothing Then
' no current word application
Set wdApp = CreateObject("Word.application")
Set wrdDoc = wdApp.Documents.Open(WDoc)
wdApp.Visible = True
Else
' word app running
For Each tmpDoc In wdApp.Documents
If StrComp(tmpDoc.FullName, WDoc, vbTextCompare) = 0 Then
' this is your doc
Set wrdDoc = tmpDoc
Exit For
End If
Next
If wrdDoc Is Nothing Then
' not open
Set wrdDoc = wdApp.Documents.Open(WDoc)
End If
End If
wdApp.Visible = True
wrdApp.Activate
Dim i As Long
Dim objShape2 As Word.Shape
Dim rng As Word.Range
Set rng = wrdDoc.Content
rng.Delete
With rng
.InsertAfter vbCr
.Collapse wdCollapseStart
Set objShape2 = ActiveDocument.Shapes.AddTextbox _
(Orientation:=msoTextOrientationHorizontal, _
Left:=400, Top:=100, Width:=250, Height:=60, Anchor:=rng)
With objShape2
.RelativeHorizontalPosition = wdRelativeHorizontalPositionColumn
.RelativeVerticalPosition = wdRelativeVerticalPositionMargin
.Left = wdShapeRight
.Top = wdShapeTop
.TextFrame.TextRange = "This is nice and shine" & vbCrLf & "222"
.TextFrame.TextRange.ParagraphFormat.Alignment = wdAlignParagraphLeft
End With
rng.Start = ActiveDocument.Content.End
For i = 1 To 40
.Text = i & vbCr
.Collapse wdCollapseEnd
Next i
End With
Another solution for you to look at.
'12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
'========1=========2=========3=========4=========5=========6=========7=========8=========9=========A=========B=========C
Option Explicit
Sub textboxtest()
Const my_doc_name As String = "mydocument.docx"
Dim my_fso As Scripting.FileSystemObject
Dim my_doc As Word.Document
Dim my_range As Word.Range
Dim counter As Long
Dim my_text_box As Word.Shape
Dim my_shape_range As Word.ShapeRange
' There is no need to test for the Word app existing
' if this macro is in a Word template or Document
' because to run the macro Word MUST be loaded
Set my_fso = New Scripting.FileSystemObject
If my_fso.FileExists(ThisDocument.Path & "\" & my_doc_name) Then
Set my_doc = Documents.Open(ThisDocument.Path & "\" & my_doc_name)
Else
Set my_doc = Documents.Add
my_doc.SaveAs2 ThisDocument.Path & "\" & my_doc_name
End If
my_doc.Activate ' Although it should already be visible
my_doc.content.Delete
Set my_text_box = my_doc.Shapes.AddTextbox( _
Orientation:=msoTextOrientationHorizontal, _
left:=400, _
top:=100, _
Width:=250, _
Height:=60)
With my_text_box
.Name = "TextBox1"
.RelativeHorizontalPosition = wdRelativeHorizontalPositionColumn
.RelativeVerticalPosition = wdRelativeVerticalPositionMargin
.left = wdShapeRight
.top = wdShapeTop
With .TextFrame
.TextRange = "This is nice and shine" & vbCrLf & "222"
.TextRange.ParagraphFormat.Alignment = wdAlignParagraphLeft
End With
End With
Set my_range = my_text_box.Parent.Paragraphs(1).Range
'FROM
'
' https://learn.microsoft.com/en-us/office/vba/api/word.shape'
' Every Shape object is anchored to a range of text. A shape is anchored
' to the beginning of the first paragraph that contains the anchoring
' range. The shape will always remain on the same page as its anchor.
my_range.Collapse Direction:=wdCollapseEnd
With my_range
For counter = 1 To 90
.Text = counter
.InsertParagraphAfter
.Collapse Direction:=wdCollapseEnd
Next
End With
End Sub
I have a report in Excel that I run every day. I pull the report from an email, do some filtering, write down some numbers and copy some of the table info from the Excel report.
The table in Excel, let's say it has data in columns A-Z. I'm trying to copy data from Excel and into Word based on certain filtering criteria. I have most of that down.
When I copy the filtered table from Excel into Word, and the table is being pasted beneath some text, the table overwrites the text in the Word document.
Const olFolderInbox As Integer = 6
'~~> Path for the attachment
Const AttachmentPath As String = "C:\Users\....."
Sub DownloadAttachmentFirstUnreadEmail()
Dim oOlAp As Object, oOlns As Object, oOlInb As Object, LastRow As Long, objDoc As Object, objWord As Object, objSelection As Object, nonProdCount As Integer, nonProdDT As Integer
Dim oOlItm As Object, oOlAtch As Object, fname As String, sFound As String, totalRowCount As Integer, wFound As String, wdRange As Word.Range, str As String, nonProdCopyToWord As Long
Dim wb As Workbook, uRng As Range
'~~> New File Name for the attachment
Dim NewFileName As String
NewFileName = "MorningOpsFile " & Format(Date, "MM-DD-YYYY")
'~~> Get Outlook instance
Set oOlAp = GetObject(, "Outlook.application")
Set oOlns = oOlAp.GetNamespace("MAPI")
Set oOlInb = oOlns.GetDefaultFolder(olFolderInbox).Folders("Folder Name Here")
Set objWord = CreateObject("Word.Application")
Set objDoc = objWord.Documents.Add
objWord.Visible = True
Set objSelection = objWord.Selection
'~~> Check if there are any actual unread emails
If oOlInb.Items.Restrict("[UnRead] = True").Count = 0 Then
MsgBox "NO Unread Email In Inbox"
Exit Sub
End If
'~~> Extract the attachment from the 1st unread email
For Each oOlItm In oOlInb.Items.Restrict("[UnRead] = True")
'~~> Check if the email actually has an attachment
If oOlItm.Attachments.Count <> 0 Then
For Each oOlAtch In oOlItm.Attachments
'~~> Download the attachment
oOlAtch.SaveAsFile NewFileName & oOlAtch.Filename
Exit For
Next
Else
MsgBox "The First item doesn't have an attachment"
End If
Exit For
Next
'~~> Mark 1st unread email as read
For Each oOlItm In oOlInb.Items.Restrict("[UnRead] = True")
oOlItm.UnRead = False
DoEvents
oOlItm.Save
Exit For
Next
'--> Search for downloaded file without knowing exact filename
sFound = Dir(ActiveWorkbook.Path & "\*File Search String*.xlsx")
If sFound <> "" Then
Workbooks.Open Filename:=ActiveWorkbook.Path & "\" & sFound
End If
Set uRng = ActiveSheet.Range("A1:A2")
'--> Set variable for last row in sheet containing data
LastRow = Sheets("Sheet1).Cells(Rows.Count, 1).End(xlUp).Row
'--> Apply filter to look for today's changes
With Sheets("Sheet 1").Select
Range("$A$1:AB" & LastRow).AutoFilter Field:=3, Criteria1:= _
xlFilterToday, Operator:=xlFilterDynamic
'--> Get a total row count of today's changes
totalRowCount = ActiveSheet.AutoFilter.Range.Columns(1).SpecialCells(xlCellTypeVisible).Cells.Count - 1
'--> Printout total rowcount number
' MsgBox totalRowCount
Set objDoc = objWord.Documents.Open("C:\Users\....docx")
objWord.Visible = True
'objWord.Activate
objDoc.Content.Select
objDoc.Content.Delete
objWord.Selection.TypeText vbNewLine
objWord.Selection.TypeText "Good Morning All" & vbNewLine
objWord.Selection.TypeText "We have " & totalRowCount & " total current day changes" & vbNewLine
End With
'--> Filter for non-Prod changes
ActiveSheet.Range("$A$1:AB" & LastRow).AutoFilter Field:=10, Criteria1:="QA", _
Operator:=xlOr, Criteria2:="Development"
'-->Count non-Prod changes
nonProdCount = ActiveSheet.AutoFilter.Range.Columns(1).SpecialCells(xlCellTypeVisible).Cells.Count - 1
'--> Put count of non-Prod changes in Word document
objWord.Selection.TypeText "We have " & nonProdCount & " non-production changes" & vbNewLine
'--> Filter for non-Prod changes with downtime
ActiveSheet.Range("$A$1:AB" & LastRow).AutoFilter Field:=11, Criteria1:="<>", _
Operator:=xlAnd
'--> Count non-Prod changes with downtime
nonProdDT = ActiveSheet.AutoFilter.Range.Columns(1).SpecialCells(xlCellTypeVisible).Cells.Count - 1
'--> Add non-prod downtime count to Word
objWord.Selection.TypeText nonProdDT & " with downtime" & vbNewLine
'--> Copy non-Prod rows with downtime from Excel to Word
'Set uRng = Union(Range("A1:A" & LastRow).SpecialCells(xlCellTypeVisible), (Range("G1:H" & LastRow).SpecialCells(xlCellTypeVisible)))
'uRng.Copy
ActiveSheet.Range("B1:F" & LastRow).EntireColumn.Hidden = True
ActiveSheet.Range("N1:Q" & LastRow).EntireColumn.Hidden = True
ActiveSheet.Range("Z1:AB" & LastRow).EntireColumn.Hidden = True
ActiveSheet.Range("A1:Y" & LastRow).SpecialCells(xlCellTypeVisible).Copy
objWord.Selection.TypeText vbNewLine
objDoc.Content.Paste
End Sub
How can I copy the filtered table in Excel to Word without overwriting what is in the Word document?
This is your problem:
objWord.Selection.TypeText vbNewLine
objDoc.Content.Paste
objDoc.Content is the entire main body of the document - the entire "content", excluding headers, footers, any text in objects that have "text wrap" formatting, etc.
You can use objWord.Selection.Paste, analog to the first line, above.
Perferable, from a programmer's perspective, would be to work with Word Range objects. Something like:
Dim wdRange as Object
Set wdRange = objWord.Selection.Range
wdRange.InsertParagraph
wdRange.Collapse 0
wdRange.Paste
The reason this is considered preferable is that not relying on Selection is more reliable. Theoretically, something could change the Selection while your code is running. A Range will remain static. This also makes it easier to understand where things are being inserted/manipulated (the code is more "self-documenting").
'This code is inside an module of a Workbook.
Sub Notes_Email_Excel_Cells2()
Application.WindowState = xlNormal
Dim NSession As Object
Dim NDatabase As Object
Dim NUIWorkSpace As Object
Dim NDoc As Object
Dim NUIdoc As Object
Dim WordApp As Object
Dim subject As String
Dim dd As String
Dim stAttachment As String
Dim obAttachment As Object, EmbedObject As Object
Const EMBED_ATTACHMENT As Long = 1454
Dim Wb As Workbook
Dim FirstCell As Range, LastCell As Range
Dim CC(1)
CC(1) = "yyy#itc.in,"
If bIsBookOpen("Daily Beetle Count Report - MMGR.xlsx") = True Then
Set Wb = Workbooks("Daily Beetle Count Report - MMGR.xlsx")
Else
Workbooks.Open ("B:\Sangeet\Daily Beetle Count Report - MMGR.xlsx")
Set Wb = Workbooks("Daily Beetle Count Report - MMGR.xlsx")
End If
Set NSession = CreateObject("Notes.NotesSession")
'Next line only works with 5.x and above. Replace password with your password
subject = "HOT SPOTS Infestation " & Now
Debug.Print subject
Set NUIWorkSpace = CreateObject("Notes.NotesUIWorkspace")
Set NDatabase = NSession.GetDatabase("", "")
If Not NDatabase.IsOpen Then NDatabase.OPENMAIL
'Create a new Lotus Notes document
Set NDoc = NDatabase.CreateDocument
stAttachment = ActiveWorkbook.FullName
Set obAttachment = NDoc.CreateRichTextItem("stAttachment")
Set EmbedObject = obAttachment.EmbedObject(EMBED_ATTACHMENT, "", stAttachment)
With NDoc
.SendTo = "xxx#itc.in" 'CHANGE RECIPIENT EMAIL ADDRESS
.CopyTo = ""
.subject = subject
'Email body text, including marker text which will be replaced by the Excel cells
.body = "Dear All," & vbLf & vbLf & "Please find the Hotspot areas" & vbLf & vbLf & _
"**PASTE HERE**" & vbLf & vbLf & vbLf & vbLf & _
"Auto Generated Mail. Please Donot Reply."
.Save True, False
End With
'Edit the just-created document to copy and paste the Excel cells into it via Word
Set NUIdoc = NUIWorkSpace.EDITDocument(True, NDoc)
With NUIdoc
'Find the marker text in the Body item
.GotoField ("Body")
.FINDSTRING "**PASTE HERE**"
'.DESELECTALL 'Uncomment to leave the marker text in place (cells are inserted immediately before)
'Copy Excel cells to clipboard
Wb.Sheets("HOT SPOT").Activate
Sheets("HOT SPOT").Range("v2:w21").Copy 'CHANGE SHEET AND RANGE TO BE COPIED AND PASTED
'Create a temporary Word Document
Set WordApp = CreateObject("Word.Application")
WordApp.Visible = False 'True to aid debugging
WordApp.Documents.Add
'Paste into Word document and copy to clipboard
With WordApp.Selection
.PasteSpecial DataType:=10 'Enum WdPasteDataType: 10 = HTML; 2 = Text; 1 = RTF
.WholeStory
.Copy
End With
'Paste from clipboard (Word) to Lotus Notes document
.Paste
Application.CutCopyMode = False
WordApp.Quit SaveChanges:=False
Set WordApp = Nothing
.Send
.Close
End With
Set NSession = Nothing
If bIsBookOpen("Daily Beetle Count Report - MMGR.xlsx") Then
Workbooks("Daily Beetle Count Report - MMGR.xlsx").Close SaveChanges:=False
Else
End If
End Sub
Function bIsBookOpen(ByRef szBookName As String) As Boolean
On Error Resume Next
bIsBookOpen = Not (Application.Workbooks(szBookName) Is Nothing)
End Function
I'm using the VBA code here to copy all the charts and tables from an excel workbook into a new word document from a template which is pre-formatted with bookmarks (labeled Book1, Book2 etc). Unfortunately i only have a few tables but around 20 charts and if i leave a blank in the summary table for the ranges i get
Run-time error '5101':
Application-defined or object defined error
and it only copies and pastes over the charts and table before the gap.
This is my excel summary table:
Any idea how i can modify the code to prevent this?
Sorry - i'm a complete VBA noob
'You must set a reference to Microsoft Word Object Library from Tools | References
Option Explicit
Sub ExportToWord()
Dim appWrd As Object
Dim objDoc As Object
Dim FilePath As String
Dim FileName As String
Dim x As Long
Dim LastRow As Long
Dim SheetChart As String
Dim SheetRange As String
Dim BookMarkChart As String
Dim BookMarkRange As String
Dim Prompt As String
Dim Title As String
'Turn some stuff off while the macro is running
Application.ScreenUpdating = False
Application.EnableEvents = False
Application.DisplayAlerts = False
'Assign the Word file path and name to variables
FilePath = ThisWorkbook.Path
FileName = "WorkWithExcel.doc"
'Determine the last row of data for our loop
LastRow = Sheets("Summary").Range("A65536").End(xlUp).Row
'Create an instance of Word for us to use
Set appWrd = CreateObject("Word.Application")
'Open our specified Word file, On Error is used in case the file is not there
On Error Resume Next
Set objDoc = appWrd.Documents.Open(FilePath & "\" & FileName)
On Error Goto 0
'If the file is not found, we need to end the sub and let the user know
If objDoc Is Nothing Then
MsgBox "Unable to find the Word file.", vbCritical, "File Not Found"
appWrd.Quit
Set appWrd = Nothing
Exit Sub
End If
'Copy/Paste Loop starts here
For x = 2 To LastRow
'Use the Status Bar to let the user know what the current progress is
Prompt = "Copying Data: " & x - 1 & " of " & LastRow - 1 & " (" & _
Format((x - 1) / (LastRow - 1), "Percent") & ")"
Application.StatusBar = Prompt
'Assign the worksheet names and bookmark names to a variable
'Use With to group these lines together
With ThisWorkbook.Sheets("Summary")
SheetChart = .Range("A" & x).Text
SheetRange = .Range("B" & x).Text
BookMarkChart = .Range("C" & x).Text
BookMarkRange = .Range("D" & x).Text
End With
'Tell Word to goto the bookmark assigned to the variable BookMarkRange
appWrd.Selection.Goto What:=wdGoToBookmark, Name:=BookMarkRange
'Copy the data from Thisworkbook
ThisWorkbook.Sheets(SheetRange).UsedRange.Copy
'Paste into Word
appWrd.Selection.Paste
'Tell Word to goto the bookmark assigned to the variable BookMarkChart
appWrd.Selection.Goto What:=wdGoToBookmark, Name:=BookMarkChart
'Copy the data from Thisworkbook
ThisWorkbook.Sheets(SheetChart).ChartObjects(1).Copy
'Paste into Word
appWrd.Selection.Paste
Next
'Turn everything back on
Application.ScreenUpdating = True
Application.EnableEvents = True
Application.DisplayAlerts = True
Application.StatusBar = False
'Let the user know the procedure is now complete
Prompt = "The procedure is now completed." & vbCrLf & vbCrLf & "www.VBAExpress.com"
Title = "Procedure Completion"
MsgBox Prompt, vbOKOnly + vbInformation, Title
'Make our Word session visible
appWrd.Visible = True
'Clean up
Set appWrd = Nothing
Set objDoc = Nothing
End Sub
full working code is below. I've modified the code so it pastes charts as enhanched metafiles because that's what my boss wants.
'You must set a reference to Microsoft Word Object Library from Tools | References
Option Explicit
Sub ExportToWord()
Dim appWrd As Object
Dim objDoc As Object
Dim FilePath As String
Dim FileName As String
Dim x As Long
Dim LastRow As Long
Dim SheetChart As String
Dim SheetRange As String
Dim BookMarkChart As String
Dim BookMarkRange As String
Dim Prompt As String
Dim Title As String
'Turn some stuff off while the macro is running
Application.ScreenUpdating = False
Application.EnableEvents = False
Application.DisplayAlerts = False
'Assign the Word file path and name to variables
FilePath = ThisWorkbook.Path
FileName = "WorkWithExcel.doc"
'Determine the last row of data for our loop
LastRow = Sheets("Summary").Range("A" & Rows.Count).End(xlUp).Row
'Create an instance of Word for us to use
Set appWrd = CreateObject("Word.Application")
'Open our specified Word file, On Error is used in case the file is not there
On Error Resume Next
Set objDoc = appWrd.Documents.Open(FilePath & "\" & FileName)
On Error GoTo 0
'If the file is not found, we need to end the sub and let the user know
If objDoc Is Nothing Then
MsgBox "Unable to find the Word file.", vbCritical, "File Not Found"
appWrd.Quit
Set appWrd = Nothing
Exit Sub
End If
'Copy/Paste Loop starts here
For x = 2 To LastRow
'Use the Status Bar to let the user know what the current progress is
Prompt = "Copying Data: " & x - 1 & " of " & LastRow - 1 & " (" & _
Format((x - 1) / (LastRow - 1), "Percent") & ")"
Application.StatusBar = Prompt
'Assign the worksheet names and bookmark names to a variable
'Use With to group these lines together
With ThisWorkbook.Sheets("Summary")
SheetChart = .Range("A" & x).Text
SheetRange = .Range("B" & x).Text
BookMarkChart = .Range("C" & x).Text
BookMarkRange = .Range("D" & x).Text
End With
If Len(BookMarkRange) > 0 Then
'Tell Word to goto the bookmark assigned to the variable BookMarkRange
appWrd.Selection.Goto What:=wdGoToBookmark, Name:=BookMarkRange
'Copy the data from Thisworkbook
ThisWorkbook.Sheets(SheetRange).UsedRange.Copy
'Paste into Word
appWrd.Selection.Paste
End If
If Len(BookMarkChart) > 0 Then
'Tell Word to goto the bookmark assigned to the variable BookMarkChart
appWrd.Selection.Goto What:=wdGoToBookmark, Name:=BookMarkChart
'Copy the data from Thisworkbook
ThisWorkbook.Sheets(SheetChart).ChartObjects(1).Copy
'Paste into Word
'appWrd.Selection.PasteSpecial ppPasteEnhancedMetafile
appWrd.Selection.PasteSpecial Link:=False, DataType:=wdPasteEnhancedMetafile, _
Placement:=wdInLine, DisplayAsIcon:=False
End If
Next
'Turn everything back on
Application.ScreenUpdating = True
Application.EnableEvents = True
Application.DisplayAlerts = True
Application.StatusBar = False
'Let the user know the procedure is now complete
Prompt = "The procedure is now completed." & vbCrLf & vbCrLf & "www.VBAExpress.com"
Title = "Procedure Completion"
MsgBox Prompt, vbOKOnly + vbInformation, Title
'Make our Word session visible
appWrd.Visible = True
'Clean up
Set appWrd = Nothing
Set objDoc = Nothing
End Sub
There are multiple problems with this code, including the fact that if you had more ranges than charts it would only copy as many ranges as there was charts.
But to quickly fix your problem, replace
'Tell Word to goto the bookmark assigned to the variable BookMarkRange
appWrd.Selection.Goto What:=wdGoToBookmark, Name:=BookMarkRange
'Copy the data from Thisworkbook
ThisWorkbook.Sheets(SheetRange).UsedRange.Copy
'Paste into Word
appWrd.Selection.Paste
'Tell Word to goto the bookmark assigned to the variable BookMarkChart
appWrd.Selection.Goto What:=wdGoToBookmark, Name:=BookMarkChart
'Copy the data from Thisworkbook
ThisWorkbook.Sheets(SheetChart).ChartObjects(1).Copy
'Paste into Word
appWrd.Selection.Paste
with
if len (BookMarkRange) > 0 then
'Tell Word to goto the bookmark assigned to the variable BookMarkRange
appWrd.Selection.Goto What:=wdGoToBookmark, Name:=BookMarkRange
'Copy the data from Thisworkbook
ThisWorkbook.Sheets(SheetRange).UsedRange.Copy
'Paste into Word
appWrd.Selection.Paste
end if
if len(BookMarkChart) > 0 then
'Tell Word to goto the bookmark assigned to the variable BookMarkChart
appWrd.Selection.Goto What:=wdGoToBookmark, Name:=BookMarkChart
'Copy the data from Thisworkbook
ThisWorkbook.Sheets(SheetChart).ChartObjects(1).Copy
'Paste into Word
appWrd.Selection.Paste
end if