Using VBA, how can I write text of different heading levels in Word from Excel? - vba

Using VBA 2007, how can I create a Word document from Excel and write text of different headings (heading1, heading2, normal) so that the headings would appear in the document map?

This example will run from Excel. It uses Early Binding so you need to ensure you have a reference to Word set in the VBA References (Tools->References).
Word can be a fickle best with putting text in the document. Generally it needs to go a the currently selected point. You can use Bookmarks and/or field codes to put text in different locations within a document.
Sub MakeWordDocumentWithHeadings()
Dim wdApp As Word.Application, wdDoc As Word.Document
'Use on error resume next so VBA doesn't produce an error if it can't find Word Open
On Error Resume Next
Set wdApp = GetObject(, "Word.Application")
'If it is nothing the open a new instance of word
If wdApp Is Nothing Then Set wdApp = New Word.Application
'Reset the errors
On Error GoTo 0
'Add a new document
Set wdDoc = wdApp.Documents.Add
'Word works by the location of the 'selection'
wdApp.Selection.Style = ActiveDocument.Styles("Heading 1")
wdApp.Selection.TypeText Text:="Heading One"
wdApp.Selection.TypeParagraph
wdApp.Selection.Style = ActiveDocument.Styles("Heading 2")
wdApp.Selection.TypeText Text:="Heading Two"
wdApp.Selection.TypeParagraph
wdApp.Selection.Style = ActiveDocument.Styles("Heading 3")
wdApp.Selection.TypeText Text:="Heading Three"
wdApp.Selection.TypeParagraph
'Save close or whatever here
'Always set objects to nothing.
Set wdDoc = Nothing
Set wdApp = Nothing
End Sub

Related

Opening and then breaking links to a Word document from Excel

I have an excel spreadsheet that is linked to multiple Word documents (.docx), which act as templates.
I have written a macro that opens the required word template from excel (the template chosen is dependent on the value of cell M17). The links in the word template update automatically as word opens. I am then trying to break the links of the document that I opened. This is what I have so far:
Function FnOpeneWordDoc()
Dim objWord
Dim objDoc
Dim path As String
path = Range("M17")
Set objWord = CreateObject("Word.Application")
Set objDoc = objWord.Documents.Open("*file_path*" & path & ".docx")
objWord.Visible = True
Application.Wait (Now + TimeValue("0:00:30"))
objDoc.Fields.Unlink
End Function
I suspect that it isn't working because the macro is trying to break the links before the document has fully loaded (and therefore the existing links haven't had a chance to update?), which is why I added the wait. Unfortunately this doesn't seem to be the solution.
The above code would have been fine, but I didn't realise that the code had to be different if the text was in textboxes.
I struggled to break the links within textboxes, and kept getting run-time error 438. However, I found a workaround:
I wrote a sub in the word documents:
Sub Unlink
Selection.Fields.Unlink
End Sub
I then called this macro from my excel document:
Sub FnOpeneWordDoc()
Dim objWord
Dim objDoc
Set objWord = CreateObject("Word.Application")
Set objDoc = objWord.Documents.Open("filename")
objWord.Visible = True
For i = 1 To objDoc.Shapes.Count
objDoc.Shapes(i).Select
objDoc.Application.Run ("unlink")
Next
End Sub

Check if Word document closed from Excel VBA

I'm performing several prints of embedded Word document witch has some fields linked to some cells, by my PrintOut macro, in a For..Next loop, as below.
I need after each print task, that the program wait for document to close and then doing the next print.
In this situation I receive error. Can anyone help ?
Sub contract()
Dim i As Integer
For i = 1 To 100
Cells(Sheets("SheetName").ListObjects("StaffInfo").ListRows.Count + 9, 8).Value = i
General.PrintIt ("EmbeddedDoc") 'Doc has many linked fields
Next i
End Sub
Print method
Sub PrintIt(P As String)
Dim objWord As Object
Dim ObjDoc As Object
Dim Oshp As Object
Application.ScreenUpdating = False
ActiveSheet.OLEObjects(P).Activate
Set objWord = GetObject(, "Word.Application")
objWord.Visible = False
Set ObjDoc = objWord.ActiveDocument
ObjDoc.Fields.Update
For Each Oshp In ObjDoc.Content.ShapeRange
Oshp.TextFrame.TextRange.Fields.Update
Next
ObjDoc.PrintOut Background:=False
ObjDoc.PrintOut
objWord.Quit SaveChanges:=False
Application.ScreenUpdating = True
End Sub 'Print it
Introduced problem was solved by this code.
Above code do open and close embedded word document, 100 times, and that problem was happening in close document.
Exactly, I cant understand why closing document immediately after printout command that where after open and update fields, generating that error.
Thus I cleaned the problem ask!
By integrating "PrintIt" method in "Contract" method that is parent of that, without calling "PrintIt" for each document printout, embedded document opens each one, perform doc links updating and printing 100 times in for next loop and close word app and document, at last, each one too.
In short, I cant find reason of problem in several Open-Print-Close document in order immediately; But i change the algorithm to Open-Several print-Close and problem been cleaned!
Sub contract()
Application.ScreenUpdating = False
Sheets("SheetName").Unprotect
'Declare variables
Dim i As Integer
Dim objWord, ObjDoc As Object
'Core
ActiveSheet.OLEObjects("Contract").Activate
Set objWord = GetObject(, "Word.Application")
objWord.Visible = False
Set ObjDoc = objWord.ActiveDocument
For i = 1 To 100
Cells(x, y).Value = i 'A specific cell that
' word embedded document fields are linked to
'corresponding fields they values change
'by changing this cell.
ObjDoc.Fields.Update
ObjDoc.PrintOut Background:=False
ObjDoc.PrintOut
Next i
objWord.Quit SaveChanges:=False
Sheets("SheetName").Protect AllowFiltering:=True
End Sub

Copy and paste on new page

I am trying to write a code that copies the contents of multiple worksheets in a single workbook into a single word document. I want the content of each worksheet to be on its own page, but right now, my code is just copying and pasting over each other instead of going onto a new page and pasting. I've tried going to the end of the document but it isn't working... Any advice would be helpful.
Sub ToWord()
Dim ws As Worksheet
Dim Wkbk1 As Workbook
Set Wkbk1 = ActiveWorkbook
Application.ScreenUpdating = False
Application.DisplayAlerts = False
Application.EnableEvents = False
For Each ws In ActiveWorkbook.Worksheets
ws.Range("A1:A2").Copy
Dim wdapp As Object
Dim wddoc As Object
Dim Header As Range
Dim strdocname As String
'file name & folder path
On Error Resume Next
'error number 429
Set wdapp = GetObject(, "Word.Application")
If Err.Number = 429 Then
Err.Clear
'create new instance of word application
Set wdapp = CreateObject("Word.Application")
End If
wdapp.Visible = True
'define paths to file
strdocname = "C:\Doc.doc"
If Dir(strdocname) = "" Then
MsgBox "The file" & strdocname & vbCrLf & "was not found " & vbCrLf & "C:\Doc.doc", vbExclamation, "The document does not exist "
Exit Sub
End If
wdapp.Activate
Set wddoc = wdapp.Documents(strdocname)
If wddoc Is Nothing Then Set wddoc = wdapp.Documents.Open(strdocname)
'must activate to be able to paste
wddoc.Activate
wddoc.Range.Paste
Next ws
'Clean up
Set wddoc = Nothing
Set wdapp = Nothing
Application.ScreenUpdating = True
Application.DisplayAlerts = True
Application.EnableEvents = True
End Sub
you can just use:
wddoc.Range(i).Paste
incrementing i by 1 after each image. that pastes them one after another.
or more simply:
wddoc.Range(wddoc.Characters.Count-1).Paste
then could get more complicated and add a page break manually in between each if images are small to ensure a new page for each:
wddoc.Range(wddoc.Characters.Count-1).InsertBreak Type:=7
https://msdn.microsoft.com/en-us/library/office/ff821608.aspx
EDIT
First, I incorrectly assumed the "wddoc.range" property would get wherever the cursor is. This is not true. You need to use the code provided by Miss Palmer (and replicated below).
However, there is an additional issue I didn't notice at first. Your loop is set incorrectly. You are looping through and continually reopening the word doc. You need to move these lines:
For Each ws In ActiveWorkbook.Worksheets
ws.Range("A1:A2").Copy
so that they are immediately above the
wddoc.Range(wddoc.Characters.Count - 1).Paste
line. This will cause the loop to be executed properly and only open the word doc once.
Also (again, per Miss Palmer), you want to put this:
wddoc.Range(wddoc.Characters.Count - 1).Paste
wddoc.Range(wddoc.Characters.Count - 1).InsertBreak (wdPageBreak)after the line:
instead of the ".range.paste" that you originially had.
The other issue with your "Selection" line is that you did not specify the application you wanted to use. This was using Excel's selection by default as it was being run from Excel.
This code assumes that you have a word document that doesn't have enough pages. Otherwise you could likely use the code you want, but it's unclear why you would have a blank word document with many pages. You'd still need to specify the app you want to move to the next page in, so put "wdapp." before the selection line.
http://word.tips.net/T000120_Jumping_to_the_Start_or_End_of_a_Document.html

Selecting and copying Outlook email body with a VBA macro

I'm a beginner to VBA macros in Excel, and this is the first attempt in Outlook, but here's what I am trying to do:
In Outlook 2010, assign a macro to a button that, when pushed,
Gets the entire body of the active email
Copies the body including all formatting and html to the clipboard
Opens a new word document
Pastes the content of the clipboard to this word doc
Clears the clipboard
So far, all I have are steps 1 and 3 (and I wonder if I'm going about this the wrong way in step 1) below:
Sub pasteToWord()
Dim activeMailMessage As Outlook.MailItem 'variable for email that will be copied.
Dim activeBody
Dim clearIt As String 'Intended to eventually clear clipboard.
'Code to get to the body of the active email.
If TypeName(ActiveExplorer.Selection.Item(1)) = "MailItem" Then _
Set activeMailMessage = ActiveExplorer.Selection.Item(1)
activeBody = activeMailMessage.Body
'MsgBox activeBody
'^This displayed what I want in plaintext form,
'so I think im on the right track
'Code to copy selection to clipboard
'Code to open new Word doc
Set WordApp = CreateObject("Word.Application")
WordApp.Documents.Add
WordApp.Visible = True
'Code to paste contents of clipboard to active word document
'Code to clear clipboard
End Sub
Any guidance to fill in the blanks above would be much appreciated.
Edit:
Here is what has come the closest so far, thanks to David Zemens. I think I am missing some reference though, because my compiler doesn't understand "DataObject" for the ClearClipboard() function. It does copy and paste into word with formatting though, as is below (though I had to comment out the last function to avoid errors):
Sub pasteToWord()
Dim WordApp As Word.Application 'Need to link Microsoft Word Object library
Dim wdDoc As Word.Document 'for these to be understood by compiler
Dim activeMailMessage As Outlook.MailItem
Dim activeBody As String
If TypeName(ActiveExplorer.Selection.Item(1)) = "MailItem" Then
'Get a handle on the email
Set activeMailMessage = ActiveExplorer.Selection.Item(1)
'Ensure Word Application is open
Set WordApp = CreateObject("Word.Application")
'Make Word Application visible
WordApp.Visible = True
'Create a new Document and get a handle on it
Set wdDoc = WordApp.Documents.Add
'Copy the formatted text:
activeMailMessage.GetInspector().WordEditor.Range.FormattedText.Copy
'Paste to the word document
wdDoc.Range.Paste
'Clear the clipboard entirely:
Call ClearClipBoard
End If
End Sub
Public Sub ClearClipBoard()
Dim oData As New DataObject 'object to use the clipboard -- Compiler error,
'I think I'm missing a reference here.
oData.SetText Text:=Empty 'Clear
oData.PutInClipboard 'take in the clipboard to empty it
End Sub
This method will copy the formatted text from the selected mailitem, and paste it in to word document:
Dim WordApp As Word.Application
Dim wdDoc As Word.Document
Dim activeMailMessage As MailItem
If TypeName(ActiveExplorer.Selection.Item(1)) = "MailItem" Then
'Get a handle on the email
Set activeMailMessage = ActiveExplorer.Selection.Item(1)
'Ensure Word Application is open
Set WordApp = CreateObject("Word.Application")
'Make Word Application visible
WordApp.Visible = True
'Create a new Document and get a handle on it
Set wdDoc = WordApp.Documents.Add
'Copy the formatted text:
activeMailMessage.GetInspector().WordEditor.Range.FormattedText.Copy
'Paste to the word document
wdDocument.Range.Paste
'Clear the clipboard entirely:
Call ClearClipBoard
End If
NOTE Clearing the clipboard entirely can be done pretty easily with a function like the one described here:
Public Sub ClearClipBoard()
Dim oData As New DataObject 'object to use the clipboard
oData.SetText Text:=Empty 'Clear
oData.PutInClipboard 'take in the clipboard to empty it
End Sub
You can use the Word object model when dealing woth item bodies.
Word is used as an email editor in Outlook. The WordEditor property of the Inspector class returns an instance of the Document class from the Word object model which represents the Body of your email. See Chapter 17: Working with Item Bodies for more information.
As you may see, there is no need to use any extra tools or classes (Clipboard and etc.). You can copy the document using built-in mechanisms or save the document as is.

Excel VBA: Copy XL named range values to DOC bookmarks, then export to PDF

I'm trying to copy the values from a named range in Excel to a bookmark in Word. I found this code on the web that does it in Excel VBA, but I'm getting an Error 13.
Set pappWord = CreateObject("Word.Application")
Set docWord = pappWord.Documents.Add(Path)
'Loop through names in the activeworkbook
For Each xlName In wb.Names
'if xlName's name is existing in document then put the value in place of the bookmark
If docWord.Bookmarks.Exists(xlName.Name) Then
docWord.Bookmarks(xlName.Name).Range.Text = Range(xlName.Value)
End If
Next xlName
'Activate word and display document
With pappWord
.Visible = True
.ActiveWindow.WindowState = 0
.Activate
End With
I know that the line that is causing the error is:
docWord.Bookmarks(xlName.Name).Range.Text = Range(xlName.Value)
What am i doing wrong? Also, how & where would I code so that I can export the doc to PDF?
Thanks in advance.
Note: I've already selected the reference to the Microsoft Word (version number 14) Object model in Excel
so I use it to accomplish this task but taking an image from formatted Excel table.
Sub FromExcelToWord()
Dim rg As Range
For Each xlName In wb.Names
If docWord.Bookmarks.Exists(xlName.Name) Then
Set rg = Range(xlName.Value)
rg.Copy
docWord.ActiveWindow.Selection.Goto what:=-1, Name:=xlName.Name
docWord.ActiveWindow.Selection.PasteSpecial link:=False, DataType:=wdPasteEnhancedMetafile, Placement:= _
0, DisplayAsIcon:=False
End If
Next xlName
End Sub
Just curious... Why are you adding a document rather than opening the relevant doc which has the bookmarks? Try this code (I usually test the code before posting but I haven't tested this particular code. Just quickly wrote it)
Also I am using Late Binding so no reference to the Word Object Library is required.
Sub Sample()
Dim wb As Workbook
Dim pappWord As Object, docWord As Object
Dim FlName As String
Dim xlName As Name
FlName = "C:\MyDoc.Doc" '<~~ Name of the file which has bookmarks
'~~> Establish an Word application object
On Error Resume Next
Set pappWord = GetObject(, "Word.Application")
If Err.Number <> 0 Then
Set pappWord = CreateObject("Word.Application")
End If
Err.Clear
On Error GoTo 0
Set docWord = pappWord.Documents.Open(FlName)
Set wb = ActiveWorkbook
For Each xlName In wb.Names
'if xlName's name is existing in document then put the value in place of the bookmark
If docWord.Bookmarks.Exists(xlName.Name) Then
docWord.Bookmarks(xlName.Name).Range.Text = Range(xlName).Value
End If
Next xlName
'Activate word and display document
With pappWord
.Visible = True
.ActiveWindow.WindowState = 0
.Activate
End With
End Sub
EDIT
Changed
Range(xlName.Value)
to
Range(xlName).Value
Now the above code is TRIED AND TESTED :)