Find / replace text in embedded word object code stopped working - excel

I have used this code successfully to replace content in an embedded word object from excel. I copied the code for a new excel file but now it doesn't work. It opens the file but doesn't replace although I can see that it IS finding the right text and replacement text. I'm kind of lost as to what is happening.
Dim strFindText As Range
Dim strReplaceText As Range
Dim nSplitItem As Long
Set strFindText = ActiveWorkbook.Worksheets("Utilisation Form").Range("c11:c20")
Set strReplaceText = ActiveWorkbook.Worksheets("Utilisation Form").Range("a11:a20")
nSplitItem = strFindText.Count
Debug.Print strFindText.Item(0)
For Each sh In ThisWorkbook.Sheets("Utilisation Form").Shapes
If sh.Name <> "Object 1" Then sh.Delete
Next
Set urobj = ThisWorkbook.Sheets("Utilisation Form").OLEObjects("Object 1")
Set wordtemp = urobj.Duplicate
wordtemp.Verb Verb:=xlOpen
Set wordtemp2 = wordtemp.Object
For x = 1 To nSplitItem
With wordtemp2.Content.Find
.Forward = True
.Text = strFindText.Item(x)
.ClearFormatting
.Replacement.Text = strReplaceText.Item(x)
.Execute Replace:=wdReplaceAll
End With
Next x
End Sub
Thanks for the support

When the early-binding technology is used in the code you need to add a corresponding COM reference to be able to use data types. Otherwise, you need to declare everything from the Word object model as Object in the code and use the late-binding technology.
To use early binding on an object, you need to know what its v-table looks like. In Visual Basic, you can do this by adding a reference to a type library that describes the object, its interface (v-table), and all the functions that can be called on the object. Once that is done, you can declare an object as being a certain type, then set and use that object using the v-table. For example, if you wanted to Automate Microsoft Office Excel using early binding, you would add a reference to the Microsoft Excel X.0 Object Library from the Project|References dialog, and then declare your variable as being of the type Excel.Application. From then on, all calls made to your object variable would be early bound.
Read more about that in the Using early binding and late binding in Automation article.

Related

Change Word table format from Excel

I have a macro in Excel that creates a Word where some Excel tables are copied, and I have this code for format changing:
Set WordTable = myDoc.Tables(i)
With WordTable
.AutoFitBehavior (wdAutoFitWindow)
.Shading.Texture = wdTextureNone
.Shading.BackgroundPatternColor = wdColorWhite
.Range.Font.TextColor = wdColorBlack
.Range.ParagraphFormat.SpaceAfter = 0
End With
Everything works properly except when the reference "Microsoft Word Object Library" is not set. In this case, the shading turns black for some reason. Is there any way to solve it, apart from set this reference?
The problem is that this macro is part of a bigger Excel program that the user installs in their own PCs, so shouldn't be able to work with VBA.
The issue is, if you use that code in Excel and "Microsoft Word Object Library" is not set, Excel does not know the Word constants wdAutoFitWindow, wdTextureNone, wdColorWhite and wdColorBlack. Instead Excel will treat them as variables and since you did not initialize them each of it has the value 0.
Make sure you use Option Explicit so you get notified if you use something that is not defined!
I recommend always to activate Option Explicit:
In the VBA editor go to Tools › Options › Require Variable Declaration.
To solve the issue, you need to either replace them with their actual value (you can find the values for the enumerations here: Enumerations (Word)) or define them as constants in Excel.
Option Explicit
Public Sub Example
Const wdAutoFitWindow As Long = 1
Const wdTextureNone As Long = 0
Const wdColorWhite As Long = 16777215
Const wdColorBlack As Long = 0
Set WordTable = myDoc.Tables(i)
With WordTable
.AutoFitBehavior wdAutoFitWindow
.Shading.Texture = wdTextureNone
.Shading.BackgroundPatternColor = wdColorWhite
.Range.Font.TextColor = wdColorBlack
.Range.ParagraphFormat.SpaceAfter = 0
End With
End Sub
You can also define them outside the scope of a procedure if you plan to use them in multiple procedures/functions. Make sure to use Option Explicit on top of every module to ensure all variables are declared properly or you will quickly run into issues again.

What about "Application" as default object in Excel VBA?

I have just written this easy macro in Excel VBA for merging a group of selected cells:
Sub Macro_Merge()
Dim Temp As String
Dim S As Variant
Temp = ""
For Each S In Selection
If Temp = "" Then
Temp = CStr(S.Value)
Else:
Temp = Temp + "," + CStr(S.Value)
End If
Next
Selection.Merge
Selection.Value = Temp
Selection.VerticalAlignment = xlTop
End Sub
This works fine, but I always see that annoying dialog box, warning me about loosing data while merging (which is exactly what I'm trying to avoid in my macro).
I can get rid of that dialog box, configuration the Application's DisplayAlerts property:
Application.DisplayAlerts = False
Selection.Merge
Selection.Value = Temp
Application.DisplayAlerts = True
This is working fine.
So, as Application is the default object, I tried to clean up my code, as follows:
DisplayAlerts = False
Selection.Merge
Selection.Value = Temp
DisplayAlerts = True
As you see, I simply omit mentioning the Application object. This is something which is allowed and I've done in the past. (If not in VBA, then Delphi, maybe?)
... but to my surprise, the dialog box appears again (although pressing F1 brings me to the official "Application.DisplayAlerts" documentation).
This leaves me with a simple question:
If a simple DisplayAlerts = ... does not equal Application.DisplayAlerts = ... anymore, what does it mean and how can I use it?
For your information, I'm working with Excel-365.
DisplayAlerts is an undeclared variable.
Certain Application properties and methods can (effectively) have the Application omitted:
ActiveCell, ActiveSheet, ActiveWorkbook, ActiveWindow, Addins, Charts, Selection, etc.
Calculate, Evaluate, Intersect, Run, Union, etc.
(but see this answer why/how this works):
A boolean property such as DisplayAlerts (EnableEvents, ScreenUpdating, etc) doesn't fall into the above category.
A golden rule in order not to fall into such a trap is the usage of Option Explicit while writing macros.
Just to add some information to the answer of #BigBen. If you write something like Workbooks or ActiveSheet in your code, VBA is not looking into the Application-object - it is looking into a (rather well hidden) object named Global.
The global object is exposing some (but not all) properties and methods of the Application-object, so ActiveSheet is referring to Application.ActiveSheet - but not because the Application has a member with this name but because the Global object defines that ActiveSheet means Application.ActiveSheet. In fact even the Application-object is accessed via the Global object.
There is hardly any information about this Global object or its concept. I found a page from Microsoft describing the Global object of MS Word, but the only explanation there is "Contains top-level properties and methods that don't need to be preceded by the Application property.". For Excel, I found this page on O'Reilly.
From time to time you get strange error messages like "Excel VBA Method 'Range' of object'_global' failed" - this is a pointer to the Global object. I would be glad to learn more about the concepts and mechanics of this object, but I am afraid that there are only very few people around that know more (except of course Mathieu Guindon AKA Mr. Rubberduck...). In daily life, we take it for granted that things like ActiveSheet simply works.

Change built-in Document properties without opening

I am attempting to run the below line of code in a sub. The purpose of the sub overall is to automatically create agendas for recurring meetings, and notify the relevant people.
'Values for example;
MtgDate = CDate("11/06/2020")
Agenda ="Z:\Business Manual\10000 Management\11000 Management\11000 Communications\Operations Meetings\11335 - OPS CCAR Performance Review Agenda 11.06.20.docx" 'NB it's a string
'and the problematic line:
Word.Application.Documents(Agenda).BuiltinDocumentProperties("Publish Date") = MtgDate
Two questions:
1) Can I assign a document property just like that without opening the document? (bear in mind this vba is running from an excel sheet where the data is stored)
2) Will word.application.documents accept the document name as a string, or does it have to be some other sort of object or something? I don't really understand Word VBA.
Attempts so far have only resulted in
runtime error 427 "remote server machine does not exist or is
unavailable"
or something about a bad file name.
Although Publish Date can be found under Insert > Quick Parts > Document Property it isn't actually a document property. It is a "built-in" CustomXML part, a node of CoverPageProperties, and can be addressed in VBA using the CustomXMLParts collection.
The CustomXML part is only added to the document once the mapped content control is inserted.
Below is the code I use.
As already pointed out for document properties the document must be open.
Public Sub WriteCoverPageProp(ByVal strNodeName As String, ByVal strValue As String, _
Optional ByRef docTarget As Document = Nothing)
'* Nodes: Abstract, CompanyAddress, CompanyEmail, CompanyFax, CompanyPhone, PublishDate
'* NOTE: If writing PublishDate set the content control to store just the date (default is date and time).
'* The date is stored in the xml as YYYY-MM-DD so must be written in this format.
'* The content control setting will determine how the date is displayed.
Dim cxpTarget As CustomXMLPart
Dim cxnTarget As CustomXMLNode
Dim strNamespace As String
If docTarget Is Nothing Then Set docTarget = ActiveDocument
strNodeName = "/ns0:CoverPageProperties[1]/ns0:" & strNodeName
strNamespace = "http://schemas.microsoft.com/office/2006/coverPageProps"
Set cxpTarget = docTarget.CustomXMLParts.SelectByNamespace(strNamespace).item(1)
Set cxnTarget = cxpTarget.SelectSingleNode(strNodeName)
cxnTarget.Text = strValue
Set cxnTarget = Nothing
Set cxpTarget = Nothing
End Sub
You cannot modify a document without opening it. In any event, "Publish Date" is not a Built-in Document Property; if it exists, it's a custom one.
Contrary to what you've been told, not all BuiltinDocumentProperties are read-only; some, like wdPropertyAuthor ("Author"), are read-write.
There are three main ways you could modify a Word document or "traditional" property (which are the ones you can access via .BuiltInDocumentProperties and .CustomProperties):
a. via the Object Model (as you are currently trying to do)
b. for a .docx, either unzipping the .docx, modifying the relevant XML part, and re-zipping the .docx.
c. For "traditional" properties, i.e. the things that you can access via .BuiltInDocumentProperties and .CustomDocumentProperties, in theory you can use a Microsoft .dll called dsofile.dll. But it hasn't been supported for a long time, won't work on Mac Word and the Microsoft download won't work on 64-bit Word. You'd also have to distribute and support it.
But in any case, "Publish Date" is not a traditional built-in property. It's probably, but not necessarily, a newer type of property called a "Cover Page Property". Those properties are in fact pretty much as "built-in" as the traditional properties but cannot be accessed via .BuiltInDocumentProperties.
To modify Cover Page properties, you can either use the object model or method (b) to access the Custom XML Part in which their data is stored. Method (c) is no help there.
Not sure where your error 427 is coming from, but I would guess from what you say that you are trying to see if you can modify the property in a single line, using the fullname of the document in an attempt to get Word to open it. No, you can't do that - you have to use GetObject/CreateObject/New to make a reference to an instance of Word (let's call it "wapp"), then (say)
Dim wdoc As Word.Document ' or As Object
Set wdoc = wapp.Documents.Open("the fullname of the document")
Then you can access its properties, e.g. for the read/write Title property you can do
wdoc.BuiltInDocumentProperties("Title") = "your new title"
wdoc.Save
If Publish Date is the Cover Page Property, once you have a reference to the Word Application and have ensured the document is open you can use code along the following lines:
Sub modPublishDate(theDoc As Word.Document, theDate As String)
' You need to format theDate - by default, Word expects an xsd:dateTime,
' e.g. 2020-06-11T00:00:00 if you only care about the date.
Const CPPUri As String = "http://schemas.microsoft.com/office/2006/coverPageProps"
Dim cxn As Office.CustomXMLNode
Dim cxps As Office.CustomXMLParts
Dim nsprefix As String
Set cxps = theDoc.CustomXMLParts.SelectByNamespace(CPPUri)
If cxps.Count > 0 Then
With cxps(1)
nsprefix = .NamespaceManager.LookupPrefix(CPPUri)
Set cxn = .SelectSingleNode(nsprefix & ":CoverPageProperties[1]/" & nsprefix & ":PublishDate[1]") '/PublishDate[1]")
If Not (cxn Is Nothing) Then
cxn.Text = theDate
Set cxn = Nothing
End If
End With
End If
Set cxps = Nothing
As for this, "Will word.application.documents accept the document name as a string", the answer is "yes", but Word has to have opened the document already. as mentioned above. Word can also accept an integer index into the .Documents collection and may accept just the name part of the FullName string.
Finally, if you do end up using a "traditional Custom Document Property", even after you have set the property and saved the document (approximately as above) you may find that the new property value has not actually saved! If so, that's down to an old error in Word where it won't save unless you have actually visited the Custom Document Property Dialog or have modified the document content in some way, e.g. adding a space at the end.

Insert a line break from Excel VBA in a Word document

From my Excel Sheet I open up a Word document. Excel generates some text within the document. Between the text I want a LineBreak.
The code results in an error message stating: parameter value out of acceptable range
Using the following documentation this should be possible using InsertBreak.
Sub InsertLineBreak()
Set wrd = CreateObject("Word.Application") 'Open Word
Set objDoc = wrd.Documents.Add 'Add new document
Set objSelection = wrd.Selection 'Select this document
objSelection.InsertBreak Type:=wdLineBreak 'Insert Break
End Sub
It works perfectly fine for me, are you sure you have added correct reference?
Try objSelection.InsertBreak Type:=6 or add Microsoft Word 16.0 Object Library reference, or declare a globar variable wdLineBreak
For future readers:
In this case the Microsoft Word 16.0 Object Library was missing.
It can be added Tools-> References and selecting the corresponding Library

Use string variable to set object variable in VBA? (Excel 2013)

I have a number of ActiveX controls/buttons on a page, and I would like to modify several of the parameters of the buttons (in a loop function).
I am fine with writing the loop function to achieve this, but cannot find a way to refer to the object using a string variable. I have set up an object variable (as per below), and a string variable to be used to change the reference for the object variable - but can't find a way to get it to work.
This is the code that does NOT work:
Private Sub TrialCode_Click()
Dim ButtonObj As Object
Dim ButtonCaption As String
Dim ButtonString As String
ButtonString = "CommandButton1"
Set ButtonObj = ButtonString
ButtonCaption = "Something"
ButtonObj.Caption = ButtonCaption 'example of the kind of parameters I want to change
End Sub
The Set ButtonObj = ButtonString is the command that fails, reporting a Type Mismatch error.
I'm working in Excel 2013.
I really hope there is some way to do this. Any help will be really appreciated!!!
The CommandButton belongs to an OLEObject
try
ButtonString = "CommandButton1"
Set ButtonObj = ActiveSheet.OLEObjects(ButtonString)
ButtonCaption = "Something"
ButtonObj.Object.Caption = ButtonCaption 'example of the kind of parameters I want to change
Note that some properties occur directly under ButtonObj, others such as Caption sit below Object

Resources