Currently this code runs with no error message but does not make the requested replacement:
Private Sub TestingButton_Click()
Dim RngFound As Range
Dim FileToProcess As Word.Document
Dim WordInstance As Object
Set WordInstance = CreateObject("Word.Application") 'For these tests I close Word first.
Set FileToProcess = WordInstance.Documents.Open("c:\sarah\junk\Attach.doc")
WordInstance.ActiveDocument.Range.Select 'Gets the whole document
Toolbox.SetupFind (WordInstance.ActiveDocument.Range)
With WordInstance.ActiveDocument.Range.Find
.Text = "rock"
.Replacement.Text = "found it!"
.Execute
End With
End Sub
In the Toolbox module:
Public Function SetupFind(ByRef RngPassed As Word.Range)
With RngPassed.Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = ""
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindContinue
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
.Execute
End With
End Function
The code above is from a test database that I created for troubleshooting. My destination is an Access form whose purpose is to extract some information from a set of documents, and place the information in a database. It was working fine. It stopped working after a particularly spectacular crash. I tried to remove a label while the form was in break mode. (I know,... deep regret) I couldn't close Access even with ctrl-break. Probably the wierdest part is that similar code now no longer works in back-up copies of the database either.
Here are things I've tried that haven't worked:
Re-import all objects into a new database.Similarly, rebuild the backend database. Break the form's code (a couple thousand lines)
into modules. Copy all the code into Notepad, save it, then create a
button in a new empty database. Recreate subs & functions by
typing, then once they exist, paste in the code from Wordpad. Reset
the form's references, which include MSWord. Make a brand new form
in the new database with one button that has only the displayed
code. Use a defined range: This all started when I got a persistent
error in RngToSearch.find.execute findtext:="reason". The error
highlighted .find and said 'Argument not optional.' So among other
things, I switched to a selection rather than a defined range in my
attempts to isolate the problem. But working with a range rather
than a selection is where I really need to end up again.
Possibly relevant observations:
Other forms in the same database with lots of backend code work fine. So do other routines in the same form.
Only one document is open. As far as I can tell, there's nothing unusual about the document. I have tried multiple documents.
The text to find exists in the document, outside of a table.
Things that have worked, as they might be clues:
In Word straight up, no code, no nothing, use 'find' to select the targeted word.
WordInstance.ActiveDocument.Range.Text = Replace(WordInstance.ActiveDocument.Range.Text, "rock", "Found it!"). Trouble is, what I ultimately need to do again is way more complex than Replace can handle.
Specify the scope for the replacement(s). The code worked for me from Access 2010 with this change in TestingButton_Click() ...
'.Execute
.Execute Replace:=wdReplaceAll
Toolbox.SetupFind also calls .Execute. Since the find and replacement text are both empty strings at that point and no scope is specified, .Execute doesn't cause harm ... but it doesn't seem useful either.
Related
I'm not technologically oriented at all, but I'm attempting to install some macros to Word for proofreading and editing. I've followed every tutorial but keep getting a syntax error on the very first line. Like Sub Confusables() or Sub PassiveWords() both keep having syntax errors. I've checked multiple times that the spelling of the macro name is correct and that I followed the tutorials exactly, but I keep getting the same error. Please help!
I've done everything I know how/that I saw advised for syntax errors online: Triple-checked Macro name. Tried deleting extraneous text identifying source of Macro. Tried using Step Into to debug. All resulted in same syntax error.
Here is one of the macros I'm trying to run:
Sub PassiveWords()
‘ Highlights passive words
‘ Written by Roger Mortis, revised by Subcortical, adapted by Jami Gold and tweaked by C.K. MacLeod; words selected from Ryan Macklin’s passive words list at http://ryanmacklin.com/2012/05/passive-voice-words/
Dim range As range
Dim i As Long
Dim TargetList
TargetList = Array(“be”, “being”, “been”, “am”, “is”, “are”, “was”, “were”, “has”, “have”, “had”, “do”, “did”, “does”, “can”, “could”, “shall”, “should”, “will”, “would”, “might”, “must”, “may”)
For i = 0 To UBound(TargetList)
Set range = ActiveDocument.range
With range.Find
.Text = TargetList(i)
.Format = True
.MatchCase = False
.MatchWholeWord = True
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
Do While .Execute(Forward:=True) = True
range.HighlightColorIndex = wdBrightGreen
Loop
End With
Next
End Sub
So I have 4 documents, 3 excel spreadsheets and 1 document. All four are in the same directory "test." All four will always remain in the same directory no matter what. However, the goal of the document is to build a report out of the three spreadsheets for multiple properties. This means that the paths would be different for every different computer that it was used on. I want a macro that will auto-update the LINK fields with the current path but I'm running into some trouble.
So far I have
SendKeys "%{F9}"
Dim path As String
path = ActiveDocument.path
Selection.Find.ClearFormatting
Selection.Find.Replacement.ClearFormatting
With Selection.Find
.Text = "C:\\Users\\Gianni\\Desktop"
.Replacement.Text = path
.Forward = True
.Wrap = wdFindContinue
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
End With
Selection.Find.Execute Replace:=wdReplaceAll
SendKeys "%{F9}"
There are two problems with this from what I can tell. If I just view the fields manually and run the code without the first SendKeys command, the find & replace works. With the first SendKeys command, however, the code doesn't replace the text with the new path. Still, the path that pastes ends up breaking the link anyway. How do I go about fixing these?
Often, it's better in Word to work with the underlying object model of a Word document, than trying to reproduce exactly what you do as a user. Understanding how Word works, from a user point-of-view is very important and there are many things you you're able to do by converting those steps into a macro. But digging into the object model is generally faster and more accurate.
Changing a LINK field code is one of those things - and like many things, there's more than one way to go about it. Here are two possibilities.
The first is close to how you're approaching it, by manipulating the field code. Note that it's not necessary, using VBA, to actually display the field code. The object model lets you manipulate it "behind the scenes".
This procedure loops all the Fields in the document, checks whether each is a LINK field. If it is, the alternate path is substituted in the field code for the original path using the VBA Replace function, then this is written to the field code.
'Assumes the linked Excel workbook is an inline shape
Sub ChangePathInLinkField()
Dim doc As word.Document
Dim fld As word.Field
Dim strSearchPath As String
Dim strReplacePath As String
Dim strNewFieldCode As String
Set doc = ActiveDocument
strSearchPath = "C:\\Users\\[user name]\\Documents\\SampleChart.xlsx"
strReplacePath = "C:\\Test\\SampleChart.xlsx"
For Each fld In doc.Fields
If fld.Type = wdFieldLink Then
strNewFieldCode = Replace(fld.code.Text, strSearchPath, strReplacePath)
fld.code.Text = strNewFieldCode
End If
Next
doc.Fields.Update
End Sub
The second procedure shows how the link path can be changed for Shapes as well as InlineShapes (if you have a Shape you can't see the LINK field). It can also be used only on InlineShapes, of course. This loops the collection, checks whether the object is a linked OLE object and, if it is, changes the path.
Which one to use will depend on your situation - test them both and decide based on that.
'Alternate: works with OLE object
Sub ChangePathInLinkedObject()
Dim doc As word.Document
Dim ils As word.InlineShape
Dim shp As word.Shape
Dim strReplacePath As String
Dim i As Long
Set doc = ActiveDocument
strReplacePath = "C:\Users\Cindy Meister\Documents\SampleChart.xlsx"
strReplacePath = "C:\Test\SampleChart.xlsx"
'For Each doesn't work because updating the field
'destroys the object, so it loops over the same object
'For this reason it's also necessary to work backwards through the document
For i = doc.InlineShapes.Count To 1 Step -1
Set ils = doc.InlineShapes(i)
If ils.Type = wdInlineShapeLinkedOLEObject Then
ils.LinkFormat.SourceFullName = strReplacePath
ils.LinkFormat.Update
End If
Next
For i = doc.shapes.Count To 1 Step -1
Set shp = doc.shapes(i)
If shp.Type = msoLinkedOLEObject Then
shp.LinkFormat.SourceFullName = strReplacePath
shp.LinkFormat.Update
End If
Next
End Sub
Instead of using SendKeys you can show field codes with:
ActiveDocument.ActiveWindow.View.ShowFieldCodes = True
and to show field values
ActiveDocument.ActiveWindow.View.ShowFieldCodes = False
That may help with your first problem.
To see how to implement relative paths in Word, check out the solution I've posted at:
http://windowssecrets.com/forums/showthread.php/154379-Word-Fields-and-Relative-Paths-to-External-Files
Since you're working with LINK fields, you'll need the macro solution there.
I'm writing a module for defining acronyms in Word documents. The script gets the acronym and definition from an Excel document. What I'm having trouble with is comparing the location of the first instance of the acronym with the location of the first instance of the full definition.
Ultimately, I need to make sure the first instance of the acronym occurs immediately after the first instance of the definition, enclosed in parentheses. After this is done, the script will need to remove subsequent instances of the definition, so I also need to figure out how to remove all but the first instance of a definition.
The end result should look something like this:
....This document is about software as a service (SaaS). SaaS is software that is hosted by someone else. Rather than installing it on your own computer, you access it through a Web browser. There are many types of SaaS.
....
How can I get the positions of these two elements and or compare their positions?
In the example above, how would I find the first instance of "SaaS" and make sure it occurred exactly two positions after (space, open parentheses) the definition (assuming the definition actually appears in the document)?
'Selects first instance of acronym. Get start and end positions of first instance of acronym.
Selection.HomeKey Unit:=wdStory
Selection.Find.Execute Acronym 'Acronym is a variable. Now that it's selected, I need to get it's start position (or the position of the cursor if the cursor is at the start of the acronym) or find a way to compare it's position to the UserSelection variable.
'Is the definition in the document?
'If no, add definition before first instance of acronym.
'If yes, get start and end positions of first instance of definition.
'Is end position of first instance of definition adjacent to start position of first instance of acronym? If not, which is first?
'If definition is first, add acronym behind definition.
'If acronym is first, add definition in front of acronym and delete remaining instances of definition.
'Highlights all instances of the acronym in green
With ActiveDocument.Content.Find
.MatchCase = True
.MatchWholeWord = False
With .Replacement
.Highlight = True
End With
.Execute Replace:=wdReplaceAll, Wrap:=wdFindContinue, FindText:=Acronym, ReplaceWith:=Acronym
End With
Any help or insight would be appreciated, as I'm completely at a loss and having no luck with Google.
-Vince
I think the following code snippet can help you:
Sub example(acronym, definition)
Selection.Find.ClearFormatting
With Selection.Find
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchWholeWord = False
.MatchWildcards = False
.MatchAllWordForms = False
End With
ActiveDocument.Range(0, 0).Select ' go to beginning of document
Selection.Find.Text = acronym
Selection.MatchSoundsLike = False
If (Not Selection.Find.Execute) Then
' acronym not found in this document
Exit Sub
End If
ActiveDocument.Range(0, 0).Select ' go to beginning of document
Selection.Find.Text = definition
Selection.MatchCase = False
Selection.MatchSoundsLike = True
While (Selection.Find.Execute)
'
Selection.Delete ' delete all definitions
'
Wend
ActiveDocument.Range(0, 0).Select ' go to beginning of document
Selection.Find.Text = acronym
Selection.MatchSoundsLike = False
If (Selection.Find.Execute) Then
Selection.InsertBefore "(" & definition & ")"
End If
End Sub
Note: I also found that authors make mistakes in the definitions (minor variations) and even an extra unintended space messes up the find.
I would like to delete certain lines from my word document using a VBA macro. Basically the (block of) text to be deleted (and replaced by "***") follows a certain pattern (below).
Bottom of Form
perma-link
Top of Form
save
Bottom of Form
[+] ....
[–] ....
Top of Form
"...." represents text that changes every block, but for sure the line starts with "[+]" or "[-]".
Please suggest a suitable macro
EDIT: In the screenshot, I would like to keep the text in yellow and delete the rest. (in the actual file, the text isn't in yellow)
PS-FYI, I tried using the example looping a find and delete row macro (for line by line deletion) but i get a runtime error 5941 with debugging option highlighting the line "selection.row.delete" in the macro.
What does this mean?
Assuming that the example list is a list of paragraphs beginnings the following code should do the trick. What you have to do is to place all 'paragraphs starting' into array arrRemove as I did for the test. If any of the mark is a special marks (see this link for additional information) you need to add \ in front of it as I did for [+] and [-]. Hope this is what you are looking for.
Sub Macro2()
Dim arrRemove As Variant
arrRemove = Array("Bottom of Form", "perma -link", "Top of Form", _
"\[+\]", "\[\-\]", "Donec", "In")
Dim i!
For i = 0 To UBound(arrRemove)
Activedocument.Range(0,0).select
Selection.Find.ClearFormatting
With Selection.Find
.Text = arrRemove(i) & "*^13"
.Replacement.Text = "" 'replace with nothing
.Forward = True
.Wrap = wdFindContinue
.MatchCase = False
.MatchWildcards = True
End With
Selection.Find.Execute Replace:=wdReplaceAll
Next i
End Sub
The above macro will remove all yellow paragraph in the following document.
I am trying to write some code that will search through all the stories including headers, footers, footnotes etc and then stop at each occurrence so the user can make a decision about it (it may or may not change), then click a button again to move to the next occurrence (like Word's Find Next).
I am aware there is some pretty tricky code for performing a search and replace using the range object and I have that code working for another part of this project, but what I can't do is make it search and stop at the selected text, then carry on looking in the different stories, it just stops at the end of the main document.
The code below looks as though it should work but even if the footnote for example has the text to be searched for, it is ignoring it. I have done a thorough search of this site and others and have found several examples for search and replace, but none for search and stop/select.
Any advice gratefully received - thank you.
Sub TestSelection()
Dim rngStory As Range
Dim docDocument As Document
Set docDocument = ActiveDocument
With docDocument
For Each rngStory In .StoryRanges
Select Case rngStory.StoryType
Case 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
Debug.Print rngStory.StoryType
With Selection.Find
.ClearFormatting
.Text = "XYZ"
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindStop
.Format = True
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
End With
Selection.Find.Execute
If Selection.Find.Found = True Then
Exit Sub
End If
End Select
Next rngStory
End With
End Sub
Whether this is your problem in this case I don't know, but while your loop iterates over all the ranges returned by StoryRanges, it does not process the entire document. It only includes the first part of each story. (So, for example, if there are several sections in your document, it will only include the header & footer from the first section).
You need to use the NextStoryRange method in order to access the entire story. Look that up in VBA help for an example loop construct. (It's a horrible API - just as bad as Range.Find!).
Also, be aware that executing a search will change the selection, so Selection.Find will suddenly be searching in the last result, rather than the entire range.