jsgreiner
asked on
How do I get the find method in a Word 2003 VBA macro to look not only in the main document, but also in text boxes?
I have a macro that looks for a string. It finds it if it is in the main document, but does not find it if it is in a text box. How do I get the find method to look everywhere in the document -- not just in the main part of the document?
The Find command from the Word menu (Edit -> Find) seems to be able to look everywhere. Is the code for that available so I can see how it does it?
The Find command from the Word menu (Edit -> Find) seems to be able to look everywhere. Is the code for that available so I can see how it does it?
ASKER
The macro always seems to start searching from the beginning of the document so it ONLY FINDS THE FIRST INSTANCE. It can find the instance if it is in a header/footer, but it WOULD NOT FIND THE INSTANCE IN A TEXT BOX (which is one of the most important places for it to look for my application). I tried some modifications as shown below, but still could not get it to work. I would like it to work like the Edit -> Find command from the Word menu works -- except that when it is done with a story, it does not wrap.
Thanks for your help with this!!!
Sub FindEveryWhere()
Dim rng As Range
Dim lclfound As Boolean
lclfound = False
For Each rng In ActiveDocument.StoryRanges
If rng.Find.Execute("9001", , , , , , True, wdFindContinue) Then
rng.Select
lclfound = True
Exit For
Else
rng.Collapse
End If
Next rng
If lclfound = False Then
MsgBox "Strng not Found"
End If
End Sub
Thanks for your help with this!!!
Sub FindEveryWhere()
Dim rng As Range
Dim lclfound As Boolean
lclfound = False
For Each rng In ActiveDocument.StoryRanges
If rng.Find.Execute("9001", , , , , , True, wdFindContinue) Then
rng.Select
lclfound = True
Exit For
Else
rng.Collapse
End If
Next rng
If lclfound = False Then
MsgBox "Strng not Found"
End If
End Sub
ASKER
I did a little more testing and found that in a new small document, the macro was able to find an instance in a text box. But in my large complex document, it does not find an instance in a text box even though the Edit -> Find command does.
I still have the problem of it only finding the first instance instead of starting the next serch where the last one ended.
I still have the problem of it only finding the first instance instead of starting the next serch where the last one ended.
If you want to find all instances, you could do it like this:
Sub FindEveryWhere()
Dim rng As Range
Dim doc As Document
Dim strText As String
strText = "dog"
Set doc = ActiveDocument
For Each rng In doc.StoryRanges
Do While rng.Find.Execute(strText, , True)
ProcessFoundRange rng
Loop
Next rng
End Sub
Sub ProcessFoundRange(rng As Range)
MsgBox "StoryType: " & rng.StoryType & ", Start: " & rng.Start
End Sub
Sub FindEveryWhere()
Dim rng As Range
Dim doc As Document
Dim strText As String
strText = "dog"
Set doc = ActiveDocument
For Each rng In doc.StoryRanges
Do While rng.Find.Execute(strText, , True)
ProcessFoundRange rng
Loop
Next rng
End Sub
Sub ProcessFoundRange(rng As Range)
MsgBox "StoryType: " & rng.StoryType & ", Start: " & rng.Start
End Sub
ASKER
The only problem with this is I can not stop and edit my document when someting is found and then continue the search for the next instance. Is there a way the macro can start the search at the position of the cursor or after the current selection? (Storing something in a registry is an option if that would be helpful to know where to start the next search.) That way I can find something, manually make whatever changes I want, and then invoke the macro again. I know this seems a bit odd, but this is one piece of a much larger semi-automated application.
As I understand it:
You want the user to select some text by placing the cursor somewhere in the document.
You then want to start a macro to search from that position onwards through the whole of the document.
When something is found, you want control to pass back to the user. Do you want the found text to be selected?
The user will then be able to do some editing, probably changing the selection.
You want the user to be able to restart the search.
Do you want the restart search to start from where the user has newly left the cursor, or from the last found place? Either is possible, but if it is the latter, there would need to be a way to distinguish between the start and restart requests.
You want the user to select some text by placing the cursor somewhere in the document.
You then want to start a macro to search from that position onwards through the whole of the document.
When something is found, you want control to pass back to the user. Do you want the found text to be selected?
The user will then be able to do some editing, probably changing the selection.
You want the user to be able to restart the search.
Do you want the restart search to start from where the user has newly left the cursor, or from the last found place? Either is possible, but if it is the latter, there would need to be a way to distinguish between the start and restart requests.
ASKER
Do you want the found text to be selected? YES
The user will then be able to do some editing, probably changing the selection. YES
You want the user to be able to restart the search. YES
Do you want the restart search to start from where the user has newly left the cursor YES
The user will then be able to do some editing, probably changing the selection. YES
You want the user to be able to restart the search. YES
Do you want the restart search to start from where the user has newly left the cursor YES
ASKER
Hello GrahamSkan -- I am extremeley interested in the answer to this one and if I could figure out how to do it, I would be happy to increase the points for it.
Thanks for all your help!!!
Thanks for all your help!!!
Thanks for the prompt. I have been working on it.
It seems that we not only have to search through each storyrange, but each textbox needs an individual search. Keeping track of that is a bit of a problem.
It seems that we not only have to search through each storyrange, but each textbox needs an individual search. Keeping track of that is a bit of a problem.
ASKER
GrahamSkan, Thanks! I will also need the same type of search capability in PowerPoint. It is a different object model thatn Word. Are you familiar with the PowerPoint object model? If yes I have a separate question open for that.
John
John
ASKER
GrahamSkan, Are the textboxes somehow numbered internally in Word? If so, could you just make the macro start at the first text box if no text box is currently selected (after it is done with all the storyranges), and if a textbox is currently selected, start the search from wherever the cursor is in the text box, and then go to the next higher numbered text box to continue the search?
TextBoxes do not have an Index property (like Sections do), but they do have an index number in the Shapes collection.
The difference is that, given a Shape object, you can't look at its Index property to identify it, but you have to step through them all looking for some characteristic that will tell you that you have the one in question. I'm still working on that.
Also, I was assuming, perhaps wrongly, that you would want the earlier boxes to be searched if nothing was found in the later ones, so it would have to be, say, boxes 5 to 10, then 1 to 4.
The difference is that, given a Shape object, you can't look at its Index property to identify it, but you have to step through them all looking for some characteristic that will tell you that you have the one in question. I'm still working on that.
Also, I was assuming, perhaps wrongly, that you would want the earlier boxes to be searched if nothing was found in the later ones, so it would have to be, say, boxes 5 to 10, then 1 to 4.
ASKER
Ideally I would like to only search forward through the text boxes to the end of the document without wrapping back to the beginning. But that might not work -- if the text boxes get numbered in the order they are inserted, then if I have a bunch of text boxes already in the document and add one mor at the begining of the document, the last text box would be at the beginning of the document. Are the text boxes numbered in the order they are created?
For my application I only really need to search the main document and the text boxes. I don't really care that much about headers, footers, footnotes, etc. It would be cool to be able to search them too, but I can easiloy live without that.
For my application I only really need to search the main document and the text boxes. I don't really care that much about headers, footers, footnotes, etc. It would be cool to be able to search them too, but I can easiloy live without that.
Sorry it took so long.
Try this and see how it fits.
Sub FindEveryWhere(Doc As Document, strText As String)
Dim rng As Range
Dim sh As Shape
Dim rng2 As Range
Dim iStories() As WdStoryType
Dim iTextBoxes() As Integer
Dim iStoryCount As Integer
Dim stProcessing As WdStoryType
Dim iCurrentStory As Integer
Dim iCurrentShape As Integer
Dim iShapeCount As Integer
Dim iTextBoxCount As Integer
Dim iCurrentTextBox As Integer
Dim i As Integer
Dim s As Integer
Dim iSh As Integer
Set rng = Selection.Range
'collect available storytypes in active document
'to avoid errors from missing types
For Each rng2 In Doc.StoryRanges
ReDim Preserve iStories(iStoryCount)
iStories(iStoryCount) = rng2.StoryType
If rng.StoryType = rng2.StoryType Then
iCurrentStory = iStoryCount
End If
iStoryCount = iStoryCount + 1
Next rng2
'step through story ranges, starting with current
For s = 0 To iStoryCount - 1
stProcessing = (iCurrentStory + s) Mod iStoryCount
If iStories(stProcessing) = wdTextFrameStory Then
'Searches through a textbox don't carry on to the next, so each
'must be searched explicitly
iCurrentShape = GetSelTextBox(Doc)
For i = 1 To Doc.Shapes.Count
If iCurrentShape = 0 Then
iSh = i
Else
'start with current text box
iSh = (i + iCurrentShape - 2) Mod Doc.Shapes.Count + 1
End If
Set sh = Doc.Shapes(iSh)
If sh.Type = msoTextBox Then
Set rng = sh.TextFrame.TextRange
'ensure that we haven't stepped back to start search
'at beginning of selection
If Selection.InRange(rng) Then
Set rng = Selection.Range
End If
If rng.Find.Execute(strText) Then
rng.Select
Exit Sub
End If
End If
Next i
Else
Set rng = Doc.StoryRanges(iStories(s tProcessin g))
If Selection.InRange(rng) Then
Set rng = Selection.Range
End If
If rng.Find.Execute(strText) Then
rng.Select
Exit Sub
End If
End If
Next s
'not found, look at beginning of current story
Set rng = Selection.Range
If rng.Find.Execute(strText, Wrap:=wdFindContinue) Then
rng.Select
Exit Sub
End If
MsgBox "Search Complete"
End Sub
Try this and see how it fits.
Sub FindEveryWhere(Doc As Document, strText As String)
Dim rng As Range
Dim sh As Shape
Dim rng2 As Range
Dim iStories() As WdStoryType
Dim iTextBoxes() As Integer
Dim iStoryCount As Integer
Dim stProcessing As WdStoryType
Dim iCurrentStory As Integer
Dim iCurrentShape As Integer
Dim iShapeCount As Integer
Dim iTextBoxCount As Integer
Dim iCurrentTextBox As Integer
Dim i As Integer
Dim s As Integer
Dim iSh As Integer
Set rng = Selection.Range
'collect available storytypes in active document
'to avoid errors from missing types
For Each rng2 In Doc.StoryRanges
ReDim Preserve iStories(iStoryCount)
iStories(iStoryCount) = rng2.StoryType
If rng.StoryType = rng2.StoryType Then
iCurrentStory = iStoryCount
End If
iStoryCount = iStoryCount + 1
Next rng2
'step through story ranges, starting with current
For s = 0 To iStoryCount - 1
stProcessing = (iCurrentStory + s) Mod iStoryCount
If iStories(stProcessing) = wdTextFrameStory Then
'Searches through a textbox don't carry on to the next, so each
'must be searched explicitly
iCurrentShape = GetSelTextBox(Doc)
For i = 1 To Doc.Shapes.Count
If iCurrentShape = 0 Then
iSh = i
Else
'start with current text box
iSh = (i + iCurrentShape - 2) Mod Doc.Shapes.Count + 1
End If
Set sh = Doc.Shapes(iSh)
If sh.Type = msoTextBox Then
Set rng = sh.TextFrame.TextRange
'ensure that we haven't stepped back to start search
'at beginning of selection
If Selection.InRange(rng) Then
Set rng = Selection.Range
End If
If rng.Find.Execute(strText) Then
rng.Select
Exit Sub
End If
End If
Next i
Else
Set rng = Doc.StoryRanges(iStories(s
If Selection.InRange(rng) Then
Set rng = Selection.Range
End If
If rng.Find.Execute(strText) Then
rng.Select
Exit Sub
End If
End If
Next s
'not found, look at beginning of current story
Set rng = Selection.Range
If rng.Find.Execute(strText, Wrap:=wdFindContinue) Then
rng.Select
Exit Sub
End If
MsgBox "Search Complete"
End Sub
ASKER
Good morning GrahamSkan,
Please send me the GetSelTextBox function that goes with the macro.
Thanks!
Please send me the GetSelTextBox function that goes with the macro.
Thanks!
That was silly of me, considering the difficulty I had in writing it.
Function GetSelTextBox(Doc As Document) As Integer
Dim sh As Shape
Dim i As Integer
For Each sh In Doc.Shapes
i = i + 1
If sh.Type = msoTextBox Then
If Selection.Range.InRange(sh .TextFrame .TextRange ) Then
GetSelTextBox = i
Exit Function
End If
End If
Next sh
End Function
Function GetSelTextBox(Doc As Document) As Integer
Dim sh As Shape
Dim i As Integer
For Each sh In Doc.Shapes
i = i + 1
If sh.Type = msoTextBox Then
If Selection.Range.InRange(sh
GetSelTextBox = i
Exit Function
End If
End If
Next sh
End Function
ASKER
Grahamskan -- Almost!
I made a simple test document with the serach string once in the main document, once in a text box, and once in a text box that is inside a drawing box.
There are two problems:
1.) It does not find the string that is in the text box that is inside the drawing box.
2.) After it finds the text in the text box, it wraps back and finds the text in the main document instead of saying the search is complete and no more instances were found.
I will always start my first search for a given string from the top of the main document. After it gets finished searching the main document, it should search the rest of the stories including the text boxes and text boxes inside drawing boxes. It should not wrap back to the beginning of the main document or to a previously found text box. It should just say it is done searching after the last text box is searched.
Thanks for your efforts -- it does seem close to working correctly.
I made a simple test document with the serach string once in the main document, once in a text box, and once in a text box that is inside a drawing box.
There are two problems:
1.) It does not find the string that is in the text box that is inside the drawing box.
2.) After it finds the text in the text box, it wraps back and finds the text in the main document instead of saying the search is complete and no more instances were found.
I will always start my first search for a given string from the top of the main document. After it gets finished searching the main document, it should search the rest of the stories including the text boxes and text boxes inside drawing boxes. It should not wrap back to the beginning of the main document or to a previously found text box. It should just say it is done searching after the last text box is searched.
Thanks for your efforts -- it does seem close to working correctly.
Not wrapping around would have been a bit easier. I'll have to simplify it a bit.
What exactly do you mean by a text box inside a drawing box? How is that achieved?
What exactly do you mean by a text box inside a drawing box? How is that achieved?
ASKER
Is there a way I can send you a very small sample document that shows what I think is a text box inside a drawing box?
Yes, indeed.
You can upload a zipped file to this site: www.ee-stuff.com
You use the same credentials as this site. Pick the Experts tab. You will need to paste the URL for the question, so that it can be found again for downloading.
Tell us here that you've done it. There is no automatic notification,
You can upload a zipped file to this site: www.ee-stuff.com
You use the same credentials as this site. Pick the Experts tab. You will need to paste the URL for the question, so that it can be found again for downloading.
Tell us here that you've done it. There is no automatic notification,
ASKER
It is uploaded under Q_22049255
Thanks,
John
Thanks,
John
Sorry John, I can't find it.
I tried it with the format in your comment and there was a rather inconspicuous message in the text in orange saying "Invalid Format".
There should be an even more inconspicuous message in blue saying "Your file has been uploaded". There will also be a couple of URLs to the file and the question files respectively
Try the URL or just 22049255.
I tried it with the format in your comment and there was a rather inconspicuous message in the text in orange saying "Invalid Format".
There should be an even more inconspicuous message in blue saying "Your file has been uploaded". There will also be a couple of URLs to the file and the question files respectively
Try the URL or just 22049255.
ASKER
GrahamSkan, Lets try this...
Your file has successfully been uploaded!
To download the file, you must be logged into EE-Stuff. Here are two pages that will display your file, if logged in:
View all files for Question ID: 22049255
https://filedb.experts-exchange.com/incoming/ee-stuff/1349-jsg-test-2-9001.ziphttps://filedb.experts-exchange.com/incoming/ee-stuff/1338-Dummy.txt
https://filedb.experts-exchange.com/incoming/ee-stuff/1339-jsg-test-tgpp-2.zip
Direct link to your file
https://filedb.experts-exchange.com/incoming/ee-stuff/1339-jsg-test-tgpp-2.zip
Your file has successfully been uploaded!
To download the file, you must be logged into EE-Stuff. Here are two pages that will display your file, if logged in:
View all files for Question ID: 22049255
https://filedb.experts-exchange.com/incoming/ee-stuff/1349-jsg-test-2-9001.ziphttps://filedb.experts-exchange.com/incoming/ee-stuff/1338-Dummy.txt
https://filedb.experts-exchange.com/incoming/ee-stuff/1339-jsg-test-tgpp-2.zip
Direct link to your file
https://filedb.experts-exchange.com/incoming/ee-stuff/1339-jsg-test-tgpp-2.zip
Ah the dreaded Drawing Canvas. When they find out that it's possible, most users prevent that from being automatically created.
Sub FindInMainAndTextBoxesNoWr ap(Doc As Document, strText As String)
Dim rng As Range
Dim sh As Shape
Dim iStories(1) As WdStoryType
Dim iStoryCount As Integer
Dim stProcessing As WdStoryType
Dim iCurrentStory As Integer
Dim iCurrentShape As Integer
Dim iShape As Integer
iStories(0) = wdMainTextStory
iStories(1) = wdTextFrameStory
Set rng = Selection.Range
If rng.StoryType = wdTextFrameStory Then
iCurrentStory = 1
End If
iStoryCount = 2
'step through story ranges, starting with current
For stProcessing = iCurrentStory To iStoryCount - 1
If iStories(stProcessing) = wdTextFrameStory Then
'Find in textbox doesn't continue to another, so each
'must be searched
iCurrentShape = GetSelTextBox(Doc)
For iShape = IIf(iCurrentShape = 0, 1, iCurrentShape) To Doc.Shapes.Count
Set sh = Doc.Shapes(iShape)
Select Case sh.Type
Case msoTextBox
Set rng = sh.TextFrame.TextRange
'ensure that we haven't stepped back to start search
'at beginning of selection
If Selection.InRange(rng) Then
Set rng = Selection.Range
End If
If rng.Find.Execute(strText) Then
rng.Select
Exit Sub
End If
Case msoCanvas
If sh.CanvasItems(1).Type = msoTextBox Then
Set rng = sh.CanvasItems(1).TextFram e.TextRang e
If Selection.InRange(rng) Then
Set rng = Selection.Range
End If
If rng.Find.Execute(strText) Then
rng.Select
Exit Sub
End If
End If
Case Else
End Select
Next iShape
Else
Set rng = Doc.StoryRanges(iStories(s tProcessin g))
If Selection.InRange(rng) Then
Set rng = Selection.Range
End If
If rng.Find.Execute(strText) Then
rng.Select
Exit Sub
End If
End If
Next stProcessing
MsgBox "Search Complete"
End Sub
Sub FindInMainAndTextBoxesNoWr
Dim rng As Range
Dim sh As Shape
Dim iStories(1) As WdStoryType
Dim iStoryCount As Integer
Dim stProcessing As WdStoryType
Dim iCurrentStory As Integer
Dim iCurrentShape As Integer
Dim iShape As Integer
iStories(0) = wdMainTextStory
iStories(1) = wdTextFrameStory
Set rng = Selection.Range
If rng.StoryType = wdTextFrameStory Then
iCurrentStory = 1
End If
iStoryCount = 2
'step through story ranges, starting with current
For stProcessing = iCurrentStory To iStoryCount - 1
If iStories(stProcessing) = wdTextFrameStory Then
'Find in textbox doesn't continue to another, so each
'must be searched
iCurrentShape = GetSelTextBox(Doc)
For iShape = IIf(iCurrentShape = 0, 1, iCurrentShape) To Doc.Shapes.Count
Set sh = Doc.Shapes(iShape)
Select Case sh.Type
Case msoTextBox
Set rng = sh.TextFrame.TextRange
'ensure that we haven't stepped back to start search
'at beginning of selection
If Selection.InRange(rng) Then
Set rng = Selection.Range
End If
If rng.Find.Execute(strText) Then
rng.Select
Exit Sub
End If
Case msoCanvas
If sh.CanvasItems(1).Type = msoTextBox Then
Set rng = sh.CanvasItems(1).TextFram
If Selection.InRange(rng) Then
Set rng = Selection.Range
End If
If rng.Find.Execute(strText) Then
rng.Select
Exit Sub
End If
End If
Case Else
End Select
Next iShape
Else
Set rng = Doc.StoryRanges(iStories(s
If Selection.InRange(rng) Then
Set rng = Selection.Range
End If
If rng.Find.Execute(strText) Then
rng.Select
Exit Sub
End If
End If
Next stProcessing
MsgBox "Search Complete"
End Sub
ASKER
GrahamSkan -- This seems to be very close! there are just a couple of things that are not working that will hopefully be very easy for you to take care of:
1. The macro aborts if there is an empty drawing canvas.
2. ***With a larger document the macro quits with no message before it finds all the 9001 markers.
3. Even if there were not changes made to the document, Word asks if you want to save the changes upon exit.
I have uploaded a document that shows the problems. To see the empty canvas problem, delete the picture on the first page but leave the canvas on the page. There should be 5 of the 9001 markers. The macros you gave me are included in the document.
Big Thanks,
John
1. The macro aborts if there is an empty drawing canvas.
2. ***With a larger document the macro quits with no message before it finds all the 9001 markers.
3. Even if there were not changes made to the document, Word asks if you want to save the changes upon exit.
I have uploaded a document that shows the problems. To see the empty canvas problem, delete the picture on the first page but leave the canvas on the page. There should be 5 of the 9001 markers. The macros you gave me are included in the document.
Big Thanks,
John
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
GrahamSkan -- You are the BEST!
Thank you, it works perfectly!!!
Thank you, it works perfectly!!!
Sub FindText()
FindEveryWhere(ActiveDocum
End Sub
Function FindEveryWhere(doc As Document, strText As String, bWholeWord As Boolean) As Range
Dim rng As Range
For Each rng In doc.StoryRanges
If rng.Find.Execute(strText, , bWholeWord) Then
Exit For
Else
rng.Collapse
End If
Next rng
Set FindEveryWhere = rng
End Function