Excel VBA to Word, Find and Replace second instance - vba

I have a bunch of word documents with three $ in them.
I need to change the second $.
Currently using this format of .find I can only change the first and last $ by toggling forward as true/false.
Which is kind of weird because looking at it you think it would loop through the entire page and replace every one (which is not what I want anyways).
I tried a different format with wdReplaceAll using .execute replace and then running the two other finds to just re-replace the first and last finds, but ran into a problem with .Expand Unit not working.
Is there a simple like +1 counter I could be using to do this? It seems like the find immediately ends after being executed and I can't figure out a way to get to my second $.
For Each oRange In WordDoc.StoryRanges
With oRange
With .Find
.Forward = True
.Text = "$"
End With
If .Find.Execute Then
.Expand Unit:=4
.Text = Price
End If
End With
Next oRange

You can use a counter in the following way
For Each oRange In WordDoc.StoryRanges
Instance = 0 ' If you want to reset for each story range
With oRange
Do While .Find.Execute("$")
Instance = Instance + 1
If Instance = 2 Then
.Expand Unit:=4
.Text = Price
Exit Do
End If
Loop
End With
Next oRange

Related

Finding by style and selecting next table

I've been tasked with writing VBA code to automate Word. I have no idea about VBA.
What is needed:
There is a standard Word document based on a template, which has a section for writing issues. Each section has a heading, and below the heading there is a table with a couple of rows.
Sections view
The overall document layout with the different sections.
Issue view
A standard issue, with a Heading for the issue title, then a table to be filled, and then a description.
How do I write a macro that will:
Review ALL the issues in the document to see if the issue has a list of "Affected Hosts" in the table below the heading that contains more than 10 hosts
If this is not the case, ignore and move on to the next one.
If this is the case, that list should be replaced with some generic text such as "See Appendix G", and then add to that Appendix G the issue title and below it the list of all those hosts.
Where I am at:
I looked for examples of code snippets, looked at the documentation, etc. This is all I have:
Sub TidyAffectedSystems()
'
' TidyAffectedSystems Macro
'
'
' Loop over all issues by finding the appropriate style (IssueHeading)
With ActiveDocument.Range
With .Find
.ClearFormatting
.Forward = True
.Format = True
.Style = "Issue Heading" ' Heading style to find
.Wrap = wdFindStop
.Execute
End With
Do While .Find.Found
MsgBox .Text
' If it is the last one, then finish looping over
If .End = ActiveDocument.Range.End Then Exit Do
.Collapse wdCollapseEnd
.Find.Execute
Loop
End With
End Sub
This code tries to find the headings based on the style ("Issue Heading"), and then print that heading. I'm doing this to make sure that at least I am finding the right sections, but that's it.
I don't know how to for example select the table below the current found item to then see if that table has more than 10 hosts and all that, and of course no idea how to then replace, take it to an Appendix and repeat.

Getting the previous Word in VBA using selection.previous wdword, 1 bug

I'm trying to write a macro to type the previous word at the cursor.
the problem is when i'm using "selection.previous wdword, 1" to get the previous character, it sometimes get the 2 previous characters and it seems like a bug. when i press "delete" button it works and it is very strange to me.
I'd glad if you help.
my ultimate goal is to create a calendar converter inside word using this code.
here is how i test it:
MsgBox Selection.previous(unit:=wdWord, Count:=1)
it is the same using next :
MsgBox Selection.Next(unit:=wdWord, Count:=1)
instead of next word, sometimes it returns the word after!
For example this is the text: during the flight on 21/3/1389
If the cursor is right after the 1389, msgbox selection.previous(1,1) would show "/"; if the cursor is after a space after 1389 it shows "1389". The problem is, I think, the space. My question is if there is any alternative to read the previous word instead of this command (Selection.previous(unit:=wdWord, Count:=1))
Word is not buggy - it's behaving as designed. Something has to tell Word where words start and end. When the cursor stands to the right of a space it's (quite logically) at the beginning of the next word. So going one word back is going to pick up 1389 instead of /.
You can work around this in your code. I'm sure there's more than one way to do it, but the following works for me in a quick test:
Sub GetPrevWord()
Dim rngSel As word.Range, rngPrev As word.Range
Set rngSel = Selection.Range
Set rngPrev = rngSel.Duplicate
rngPrev.MoveStart wdCharacter, -1
If Left(rngPrev.Text, 1) = " " Then
rngPrev.Collapse wdCollapseStart
End If
rngPrev.Select
MsgBox Selection.Previous(unit:=wdWord, Count:=1)
rngSel.Select
End Sub
What it's doing is using two Ranges: one to hold the original selection, the other to work with (rngPrev). rngPrev is extended backwards by one character and this character is evaluated. If it's a space then rngPrev is collapsed to its starting point. (Think of it like pressing the left arrow key of a selection.) In any case, rngPrev is selected and your MsgBox code is run. Finally, the original range is selected again.

Adding comments through VBA in Word includes page number field

I have a procedure that adds a comment balloon into my Word document, but I have noticed that it also adds a page number field (evident by selecting the comment balloon and selecting update field from the context menu). This only happens when the comment is added through VBA, not when I create comments manually. Is there a way that I can inhibit the page number from being added to the comment?
Code extract below:
With Selection.Find
.Text = "Approvals"
.Forward = True
.Execute
If .Found = True Then
Selection.Comments.Add Range:=Selection.Range, Text:="My comment text"
End If
End With
Here's what I used to delete those page fields. After running the bulk of the code, just before End Sub I insert the following:
For Each f In ActiveDocument.StoryRanges(wdCommentsStory).Fields
If f.Type = wdFieldPage Then
f.Delete
End If
Next
It may not be pretty, but it does the job. Unfortunately, I don't think there's a way to filter based on, say, comment author.
For Each c In ActiveDocument.Comments
If c.Author = "Macro Name" Then 'Assuming you set it when you created the comment
Debug.Print c.Range.Fields.Count 'This prints a 0
End If
Next
In case you set field codes in comments, this is a bit more selective in what it deletes. This is designed to run just after you add a particular comment. However, you can combine something similar with the above to work on all comments.
If comment.range.fields.count > 0 Then
If comment.range.fields.Item(1).Type = wdFieldPage Then
comment.range.fields.Item(1).Delete
End If
End If

Hide Text in between headers MS word 2007

I have a document that is roughly 200 pages and is essentially a list of test procedures for a specific software. Now this document has certain parts to it the pertain to different versions of the software and these parts are mixed in so their not nicely formatted in a specific order. What I would like to do is Be able to hide the parts of the document that are not needed when testing a different version. I know MS word has a font option to hide text but I would like to be able to setup up a button/hypertext link/macro that will easily hide the unneeded sections. Is this possible and how would I do it? I've started experimenting with VBA script to design my own macro but have only found a way to hide one part per shortcut hit. Is there a way to do this so all parts are effected simultaneously?
EDIT:
The document is organized like this
Version 1
Test Option button
/
Version 2
Test Option button
Check that Sample button is disabled
/
Version 1
Test Save button
/
Version 3
Test Save to USB button
/
So as you can see it's completely unorganized the code I currently have for one macro really doesn't work because instead of selecting between the two point I specify it selects the whole document.
Sub TextSelectTest()
'
' TextSelectTest Macro
' Base Test
'
With Selection.Find
.Text = "Version1"
.Forward = False
.MatchWildcards = False
.Wrap = wdFindStop
.Execute
End With
Selection.Extend
With Selection.Find
.Text = "/"
.Forward = True
.Execute
.Text = ""
End With
Selection.Extend
With Selection.Font
.Hidden = True
End With
End Sub
I don't think hiding font is most professional solution as the result is visible only for printing. But that could be the easiest in this situation especially you suggested it.
First step: set sections in your documents. It's quite easy and should be done ones in Word app. You will need to insert as many section separation marks as many parts of the document you need to manage. We will need to know which section should be/shouldn't be part of each Manual but I'll back to that later.
Second steps: Than you will need the following subroutine which will 'hide' all sections and than show appropriate ones:
Sub HideUnhide_Document_Section(secIndex As Variant)
Dim Doc As Document
Set Doc = ActiveDocument
Dim secDoc As Variant
'to hide all section first, by iteration
For Each secDoc In Doc.Sections
secDoc.Range.Font.Hidden = True
Next secDoc
'alternatively we could hide whole content without iteration:
'secDoc.Content.Font.Hidden = True
'to un-hide chosen sections
For Each secDoc In secIndex
Doc.Sections(secDoc).Range.Font.Hidden = False
Next secDoc
End Sub
And to manage your hiding process I would propose the following code:
Sub Call_Hide()
Dim arrVersion1 As Variant
'put all sections for appropriate version
arrVersion1 = Array(1, 3)
'to unhide
HideUnhide_Document_Section arrVersion1
End Sub
You could either prepare similar separate subroutine for each version or parametrize that one. It that second situation it will have to have separate arrays (arrVarsionX) for each Version of your manuals.

Word VBA Section break character in

How can you detect if the selection you have contains or is the section break character?
At the moment I select a page , collapse to the end , I want to know if the end of the page contains a section break or not.
I was going to use a method where I compare the section number at the end of the page , then move two characters forward , then check the section number. If they are different then the end of my page does have a section break - is there a better way to do it?
Perhaps something on these lines:
Function HasBreak() As Boolean
With Selection.Find
.Forward = True
.Execute FindText:="^b"
If .Found Then
HasBreak = True
End If
End With
End Function
Cheers that's a good suggestion.
The way I did it in the end was get the range of the page and section , check both "End" positions , if they were the same then the page contained the section break.