Having troubles creating and formatting Word tables from Excel VBA - excel

I'm new to MS Word VBA and am having troubles with manipulating Word documents from Excel.
The Biggest problem so far is: codes that work in Word VBA just don't work in Excel. Very strange and frustrating.
Below are the codes:
Sub abc()
Dim MSWordApp As Object, MSWordDoc As Object
Set MSWordApp = CreateObject("Word.Application")
Set MSWordDoc = MSWordApp.Documents.Add
MSWordApp.Visible = True
With MSWordDoc
With .PageSetup
.TopMargin = Application.CentimetersToPoints(0.51)
.BottomMargin = Application.CentimetersToPoints(0.51)
.LeftMargin = Application.CentimetersToPoints(0.51)
.RightMargin = Application.CentimetersToPoints(0.51)
End With
.Tables.Add Range:=.Range(0, 0), NumRows:=3, NumColumns:=2
With .Tables(1)
.Rows.Alignment = wdAlignRowCenter
.Rows.HeightRule = wdRowHeightExactly
.Rows.Height = Application.CentimetersToPoints(9.55)
.Columns.PreferredWidthType = wdPreferredWidthPoints
.Columns.PreferredWidth = Application.CentimetersToPoints(9.9)
End With
End With
MSWordApp.Activate
Set MSWordApp = Nothing
Set MSWordDoc = Nothing
End Sub
These codes work perfectly in MS Word (Of course I've changed the names of object etc when I use them in MS Word).
However, weird things happen in Excel:
1) ".Rows.Alignment = wdAlignRowCenter" just don't work at all. The Rows.Alignment of the Word table just remains as default.
2) ".Columns.PreferredWidthType = wdPreferredWidthPoints" causes error in Excel. It works fine in Word; though in Excel, an empty error msg will pop up when every time I call this property. Have no idea why...

When controlling Microsoft Word from Excel VBA, you need to add a reference to the Microsoft Word Object Library. To do so, make sure you are on your module in the VBA window, and then click Tools then References.... In the popup, scroll down to find "Microsoft Word XX.X Object Library". The version # will vary based on what you have installed.
If it doesn't show in the list, you can find it on your hard drive, by clicking "Browse..." and navigating to the program files folder where MS Word is installed, then selecting the file called "MSWORD.OLB".

Since your code is written for use with late binding, you should NOT be adding a reference to Word. Instead, you need to either define or replace the Word constants you're using. For example, instead of:
.Rows.Alignment = wdAlignRowCenter
.Rows.HeightRule = wdRowHeightExactly
you could use:
.Rows.Alignment = 1 '1=wdAlignRowCenter
.Rows.HeightRule = 2 '2=wdRowHeightExactly
Alternatively, after:
Dim MSWordApp As Object, MSWordDoc As Object
you would insert:
Const wdAlignRowCenter as Long = 1: Const wdRowHeightExactly as Long = 2
Otherwise, if you're going to set a reference to Word, you should make your code consistent with early binding throughout. For example, instead of:
Dim MSWordApp As Object, MSWordDoc As Object
Set MSWordApp = CreateObject("Word.Application")
Set MSWordDoc = MSWordApp.Documents.Add
you might use:
Dim MSWordApp As New Word.Application, MSWordDoc As Word.Document
Set MSWordDoc = MSWordApp.Documents.Add

Related

Remove part of the border of a table excel VBA

There is a set of forms that we sometimes have to fill out at work, and I'm trying to automate the task by having excel VBA recreate the form as a word document and populate with the appropriate data and print as a pdf. I'm getting stuck on removing the border line style. I want there to be no border line on the left side. I have tried different approaches, and the one that seems the most likely that it should work based on my understanding is below:
(note: ".Border(xlEdgeleft).LineStyle = xlLineStyleNone" is the line that is giving me trouble)
Sub main()
Dim objWord As Object
Dim objDoc As Object
Dim objHdrRange As Object
Dim myTable As Object
Set objWord = CreateObject("Word.Application")
Set objDoc = objWord.Documents.Add
Set objHdrRange = objDoc.Sections(1).headers(1).Range
Set myTable = objWord.activedocument.tables.Add(objHdrRange, 5, 5)
With myTable
.Borders.enable = True
.Border(xlEdgeleft).LineStyle = xlLineStyleNone
‘more code goes here later
End With
Set objDoc = Nothing
Set objHdrRange = Nothing
objWord.Quit
End Sub
xlEdgeLeft and xlLineStyleNone are from the Excel Object Model, not from the Word Object Model, and you need the latter.
Since you are late-binding, you could add the following lines:
Const wdBorderLeft As Long = -2
Const wdLineStyleNone As Long = 0
and replace xlEdgeLeft and xlLineStyleNone with these, respectively.
See the WdBorderType and WdLineStyle enum docs for more detail.

Creating a multilevel list in Outlook from Excel VBA

I am working with excel to process user input and then output an standardized email based on the input, and then take that formatted text and save it to a variable to later add it to the clipboard for ease of entry into a system we use for internal documentation.
I have a functioning approach using HTML for the email format, but that doesn't resolve my intent to have it copy the code to the clipboard or variable as the HTML tags are copied as well. I'm hoping to get the functionality of Word's bullet lists so I've been trying to adapt the MS Word code in a way that can be called on demand.
I currently have the default excel libraries, form library and object library for Word and Outlook added to the program.
My goal is to pass an array list built on excel tables through the Word list and have it format and write the text to Word editor in an outlook draft. There will be varying number of sections (No more than 6) needed to be written, typically no more than 10 items per section, usually less. So I intend to have other sub/functions call this to format each section as needed.
Attached is an example of output for this section, along with an example of where the data is coming from. Each section will have it's own sheet in Excel. The second level of the list for each section will come from a separate sheet.
I included a portion of the actual code showing the startup of a new outlook draft and entry of text. EmailBody() currently just handles any text outside of these sections, and calls a separate function for each section to parse the tables (currently as unformatted text, and only inputting line breaks).
Output Example
Data source example
Sub Email()
Dim eTo As String
eTo = Range("H4").Value
Dim myItem As Object
Dim myInspector As Outlook.Inspector
Dim wdDoc As Word.Document
Dim wdRange As Word.Range
Set myItem = Outlook.Application.CreateItem(olMailItem)
With myItem
.To = eTo
.Bcc = "email"
.Subject = CNum("pt 1") & " | " & CNum("pt 2")
'displays message prior to send to ensure no errors in email. Autosend is possible, but not recommended.
.Display
Set myInspector = .GetInspector
'Obtain the Word.Document for the Inspector
Set wdDoc = myInspector.WordEditor
If Not (wdDoc Is Nothing) Then
Set wdRange = wdDoc.Range(0, wdDoc.Characters.Count)
wdRange.InsertAfter (EmailBody(CNum("pt 1"), CNum("pt 2")))
End If
'[...]
end with
end sub
Multilevel list code I am struggling to adapt. I keep getting an error on the commented out section of code, and unsure of how to properly correct it so that it both functions and can be called on demand:
Run-time error '450': Wrong number of arguments or invalid property
assignment
Sub testList()
Dim arr1 As Object
Set arr1 = CreateObject("System.Collections.ArrayList")
With arr1
.Add "test" & " $100"
.Add "apple"
.Add "four"
End With
Dim i As Long
With ListGalleries(wdBulletGallery).ListTemplates(1).ListLevels(1)
.NumberFormat = ChrW(61623)
.TrailingCharacter = wdTrailingTab
.NumberStyle = wdListNumberStyleBullet
.NumberPosition = InchesToPoints(0.25)
.Alignment = wdListLevelAlignLeft
.TextPosition = InchesToPoints(0.5)
.TabPosition = wdUndefined
.ResetOnHigher = 0
.StartAt = 1
.LinkedStyle = ""
End With
ListGalleries(wdBulletGallery).ListTemplates(1).Name = ""
'Selection.Range.ListFormat.ApplyListTemplateWithLevel ListTemplate:= _
' ListGalleries(wdBulletGallery).ListTemplates(1), ContinuePreviousList:= _
' False, ApplyTo:=wdListApplyToWholeList, DefaultListBehavior:= _
' wdWord10ListBehavior
'writes each item in ArrayList to document
For i = 0 To arr1.Count - 1
Selection.TypeText Text:=arr1(i)
Selection.TypeParagraph
Next i
'writes each item to level 2 list
Selection.Range.SetListLevel Level:=2
For i = 0 To arr1.Count - 1
Selection.TypeText Text:=arr1(i)
Selection.TypeParagraph
Next i
Selection.Range.ListFormat.RemoveNumbers NumberType:=wdNumberParagraph
arr1.Clear
End Sub
Please forgive me if any of this seems inefficient, or an odd approach. I literally pickup up VBA a few weeks ago and only have a few hours of application in between my job responsibilities with what I've learned so far. Any assistance would be much appreciated.
The reason why you are getting that error is because, it is not able to resolve the object Selection. You need to fully qualify the Selection object else Excel will refer to the current selection from Excel.
You may have referenced the Word Object library from Excel but that is not enough. The simplest way to reproduce this error is by running this from Excel
Sub Sample()
Selection.Range.ListFormat.ApplyListTemplateWithLevel ListTemplate:= _
ListGalleries(wdBulletGallery).ListTemplates(1), ContinuePreviousList:= _
False, ApplyTo:=wdListApplyToWholeList, DefaultListBehavior:= _
wdWord10ListBehavior
End Sub
Here is a sample code which will work. To test this, open a word document and select some text and then run this code from Excel
Sub Sample()
Dim wrd As Object
Set wrd = GetObject(, "word.application")
wrd.Selection.Range.ListFormat.ApplyListTemplateWithLevel ListTemplate:= _
ListGalleries(wdBulletGallery).ListTemplates(1), ContinuePreviousList:= _
False, ApplyTo:=wdListApplyToWholeList, DefaultListBehavior:= wdWord10ListBehavior
End Sub
Applying this to your code. You need to work with the Word objects and fully qualify your objects like Word Application, Word Document, Word Range etc. For example
Dim oWordApp As Object, oWordDoc As Object
Dim FlName As String
FlName = "C:\MyFile.Docx"
'~~> Establish an Word application object
On Error Resume Next
Set oWordApp = GetObject(, "Word.Application")
If Err.Number <> 0 Then
Set oWordApp = CreateObject("Word.Application")
End If
Err.Clear
On Error GoTo 0
oWordApp.Visible = True
Set oWordDoc = oWordApp.Documents.Open(FlName)
With oWordDoc
'
'~~> Rest of the code here
'
End With
Utilizing Word lists, while functional in this circumstance created a certain tedium in coding due to the need to declare both Word and Outlook objects and resolve their relation to each other.
It appears I was declaring my HTML lists incorrectly in my original code. I shifted the margin of the <li> rather than nesting <ul> to step the list.
By nesting the HTML list tags you can get the same functionality of a word list and the formatting will persist when copied to other text editors. However, copying must be done after it is written to .HTMLBody.
<ul><li>Apple</li><ul><li>Fruit</li></ul></ul>
or for VBA:
.HTMLBody = "<ul><li>" & arg1 & "</li><ul><li>" & arg2 & "</li></ul></ul>"
The above will output this to .HTMLBody:
AppleFruit
To copy the text you only need to select all the text in the Outlook word editor and then assign it to the clipboard if pasting as is, or assign it to a variable if additional changes are needed before putting it in the clipboard.

VBA Using Excel 2016 to Populate Word 2016 Content Controls

I'm attempting to populate a Word.dotm document that has content control text boxes using Excel.
In Excel, I create the word objects and open the file, but I can't figure out how to actually access the controls.
In the Word doc's VBA, I can access them via
.SelectContentControlsByTitle("control").Item(1).Range.Text = "..."
but that doesn't work using an Object in Excel..
Here's the code I've tried in Excel:
Dim objWord As Object
Dim objDoc As Object
Dim objSelection As Object
Dim ctrl As Word.ContentControl
Set objWord = CreateObject("Word.Application")
Set objDoc = objWord.Documents.Add(ThisWorkbook.Path & "\MyDoc.dotm")
objWord.Visible = True
Set objSelection = objWord.Selection
'Doesn't work
objDoc.SelectContentControlsByTitle("control").Item(1).Range.Text = "..."
'Doesn't work either, but tried nonetheless
For Each ctrl In objDoc.contentcontrols
If ctrl.Title = "control" Then
ctrl.Range.Text = "!"
Exit For
End If
Next ctrl
UPDATE:
I didn't have the MS Word 16.0 Object Library referenced... the early binding and the tip to copy the template file rather than altering it directly mentioned by Timothy Rylatt now works and John Korchok's For Each loop works as well. Their additions have been added to the code above. Thank you!
Although you have declared ctrl as a ContentControl it is not a Word content control, which is why the For Each loop won't work.
If you want to use early binding you need to specify the library that the object type belongs to in the variable declaration. Some libraries have objects with the same name but which are completely different.
Dim ctrl As Word.ContentControl
NB: You should also create a document from the Word template rather than edit the template directly:
Set objDoc = objWord.Documents.Add(ThisWorkbook.Path & "\MyDoc.dotm")
In any macro, but especially in cross-program macros, avoid using the Selection object and use Range object instead. Sometimes using the range object requires a slightly less direct route to accessing the control, such as having to poll all content controls to find which one has the correct title:
Sub SetCCText()
Dim CC As ContentControl
For Each CC In ActiveDocument.ContentControls
If CC.Title = "control" Then
CC.Range.Text = "Text"
End If
Next CC
End Sub

How to create a Word paragraphformat object form an Excel macro

I create a new Word document form an Excel macro and that works fine.
Now I want to create a new ParagraphFormat object to apply to a lot of paragraphs.
Microsoft Office Dev Center gives an example for a macro in word.
Set oWord_app = CreateObject("Word.Application")
oWord_app.Documents.Add
Set oDoc = oWord_app.Documents(1)
'Micorsoft Def Center:
Dim myParaF As New ParagraphFormat
myParaF.Alignment = wdAlignParagraphCenter
myParaF.Borders.Enable = True
ActiveDocument.Paragraphs(1).Format = myParaF
To adapt this Word macro to my Excel macro I think I have to write
Dim myParaF as new oWord_app.ParagraphFormat
but that failed. What’s the right way?
Thank you for your advice. I’m sorry, but I really did not think to check the references.
I had only a reference to Microsoft Office 14.0 Object library, now I also a reference to Microsoft Word 14.0 Object Library. And now it works. The new statement is apparently necessary, otherwise I get the error “variable not known”.
I still wonder why it works only without “Word_app. ParagraphFormat” , but already with “ParagraphFormat”.
Thank you very much.
here my code in shot words:
Option Explicit
Dim Word_app As Object, oDoc As Object, bWordVorhanden As Boolean, pFormat As Object
Sub DocumentCreate()
Set Word_app = CreateObject("Word.Application")
Word_app.Visible = True
Word_app.Documents.Add
Set oDoc = Word_app.Documents(1)
Set pFormat = New ParagraphFormat
pFormat.Alignment = wdAlignParagraphCenter
pFormat.Borders.Enable = True
ActiveDocument.Paragraphs(1).Format = pFormat
With oDoc.Paragraphs(1).Range
.Font.Name = "Arial"
.Font.Size = 11
.Font.Bold = True
.ParagraphFormat.Alignment = 1 'wdAlignParagraphCenter
.InsertAfter "hello" 'Text:=Str(oDoc.Paragraphs.Count)
.ParagraphFormat.SpaceBefore = 12
.ParagraphFormat.SpaceAfter = 6
End With
End Sub

Macro using Documents.Open doesn't return a Document, but a String.

I want to open and read paragraphs of a Word document from an Excel macro. Here's the sample code that confuses me:
Sub example()
Dim docPath
docPath = "C:\temp\test.docx"
Dim wordApp
Set wordApp = CreateObject("Word.Application")
Dim doc
doc = wordApp.Documents.Open(docPath)
MsgBox (TypeName(doc)) 'This line displays String. According to the documentation it should be a Document.
End Sub
According to the documentation the Documents.Open method should return a Document that you can operate on. But when I check the type it says it has returned a String. Obviously I can't loop through the paragraphs of a string:
https://msdn.microsoft.com/en-us/vba/word-vba/articles/documents-open-method-word
Is the documentation wrong? Am I doing something wrong? More importantly, how do I loop through the paragraphs of a Word document from an Excel macro?
I'm using Office 365 if that matters and I have created a small Word document at C:\temp\test.docx.
try:
Set doc = wordApp.Documents.Open(docPath)
The String you are getting is some property of the Object you have created; might be the Name of the Object.................insert MsgBox doc to see what it is.
Although basically what is missing is a 'Set Doc = ...' you are guaranteed to get into trouble with a code like that. To prevent that some suggestions and a starter code (your code really, a little bit modified):
Add Word object reference from Tools\References and "type" your objects. Typing them would make it much easier to write code with intellisense support.
Never ever let the word object hang out there. Get rid of it when you are done.
Sample:
Dim docPath
docPath = "C:\temp\test.docx"
Dim wordApp As Word.Application
Set wordApp = CreateObject("Word.Application")
Dim doc As Word.Document
Set doc = wordApp.Documents.Open(docPath)
For i = 1 To doc.Paragraphs.Count
Cells(i, 1) = doc.Paragraphs(i).Range.Words.Count
Cells(i, 2) = doc.Paragraphs(i)
Next
wordApp.Quit
Set doc = Nothing
Set wordApp = Nothing

Resources