How to end a Do Loop in an MS Word macro - vba

I've made a macro to change the header color of a document, but when hitting the end of the document - or if there's no remaining header - I'm getting an error.
What I want is after the last header, at the end of the document, to exit my Do Loop.
Here's my code:
Sub Changecolortest5()
'
' Changecolortest5 Macro
'
'
If ActiveWindow.View.SplitSpecial <> wdPaneNone Then
ActiveWindow.Panes(2).Close
End If
If ActiveWindow.ActivePane.View.Type = wdNormalView Or ActiveWindow. _
ActivePane.View.Type = wdOutlineView Then
ActiveWindow.ActivePane.View.Type = wdPrintView
End If
Do
ActiveWindow.ActivePane.View.SeekView = wdSeekCurrentPageHeader
Selection.MoveDown Unit:=wdParagraph, Count:=1, Extend:=wdExtend
Selection.Font.Color = 8527984
ActiveWindow.ActivePane.View.NextHeaderFooter
Loop
'Exit Header and Footer
ActiveWindow.ActivePane.View.SeekView = wdSeekMainDocument
End Sub

Addressing Headers/Footers optimally doesn't work well with recorded macros, so I'm going to show you a slightly different approach than what you give us. The code below works directly with the underlying objects and applies formatting to the Range rather than a selection. This is faster and the screen doesn't "flicker".
When working with a group of things, such as Headers, it's more usual to use a For-Each loop to cycle through the group. In this case, Headers are specific to Sections, so you loop the Sections. Something along these lines:
Dim doc as Word.Document
Dim sec as Word.Section
Set doc = ActiveDocument
For Each sec in doc.Sections
sec.Headers(wdHeaderFooterPrimary).Range.Paragraphs(1).Range.Font.Color = 8527984
sec.Headers(wdHeaderFooterFirstPage).Range.Paragraphs(1).Range.Font.Color = 8527984
Next
In addition, if you look up the Help topic for Do loops you'll see that they require a test to end the loop: Do While or Do Until or Loop While or Loop Until a certain criterium is met. I'm pretty sure the code you show us must be giving you an error - when asking questions you should always include all relevant information, including any error messages...

Related

In microsoft word for replacing words with blanks

I want to make a macro that will do the following:
Highlight every nth selection.
Check that selection to ensure it is a word (and not numerical or punctuation).
Cut the word and paste it into another document.
Replace the word with a blank space.
Repeat until the end of the document.
The hard part is checking a selection to validate that it is indeed a word and not something else.
I found some code written by someone else that might work, but I don't understand how to implement it in my macro with the rest of the commands:
Function IsLetter(strValue As String) As Boolean
Dim intPos As Integer
For intPos = 1 To Len(strValue)
Select Case Asc(Mid(strValue, intPos, 1))
Case 65 To 90, 97 To 122
IsLetter = True
Case Else
IsLetter = False
Exit For
End Select
Next
End Function
Sub Blank()
Dim OriginalStory As Document
Set OriginalStory = ActiveDocument
Dim WordListDoc As Document
Set WordListDoc = Application.Documents.Add
Windows(OriginalStory).Activate
sPrompt = "How many spaces would you like between each removed word?"
sTitle = "Choose Blank Interval"
sDefault = "8"
sInterval = InputBox(sPrompt, sTitle, sDefault)
Selection.HomeKey Unit:=wdStory
Do Until Selection.Bookmarks.Exists("\EndOfDoc") = True
Selection.MoveRight Unit:=wdWord, Count:=sInterval, Extend:=wdMove
Selection.MoveRight Unit:=wdWord, Count:=1, Extend:=wdExtend
If IsLetter = True Then
Selection.Cut
Selection.TypeText Text:="__________ "
Windows(WordListDoc).Activate
Selection.PasteAndFormat (wdFormatOriginalFormatting)
Selection.TypeParagraph
Windows(OriginalStory).Activate
Else
Selection.MoveRight Unit:=wdWord, Count:=1, Extend:=wdMove
Selection.MoveRight Unit:=wdWord, Count:=1, Extend:=wdExtend
Loop
Loop
End Sub
The function should sit 'above' the rest of the code right? But I get an error 'argument not optional' when I run it.
Any ideas or tips much appreciated.
I think the code below will do most of what you want. Note that some of the comments relate to the reasons for which I discarded some of your code while others may prove helpful in understanding the present version.
Sub InsertBlanks()
' 02 May 2017
Dim Doc As Document
Dim WordList As Document
Dim Rng As Range
Dim Interval As String, Inter As Integer
Dim Wd As String
' you shouldn't care which Window is active,
' though it probably is the one you want, anyway.
' The important thing is which document you work on.
' Windows(OriginalStory).Activate
Set Doc = ActiveDocument
Application.ScreenUpdating = False
Set WordList = Application.Documents.Add
' If you want to use all these variables you should also declare them.
' However, except for the input itself, they are hardly necessary.
' sPrompt = "How many spaces would you like between each removed word?"
' sTitle = "Choose Blank Interval"
' sDefault = "8"
Do
Interval = InputBox("How many retained words would you like between removed words?", _
"Choose Blank Interval", CStr(8))
If Interval = "" Then Exit Sub
Loop While Val(Interval) < 4 Or Val(Interval) > 25
Inter = CInt(Interval)
' you can modify min and max. Exit by entering a blank or 'Cancel'.
' You don't need to select anything.
' Selection.HomeKey Unit:=wdStory
Set Rng = Doc.Range(1, 1) ' that's the start of the document
' Set Rng = Doc.Bookmarks("James").Range ' I used another start for my testing
Do Until Rng.Bookmarks.Exists("\EndOfDoc") = True
Rng.Move wdWord, Inter
Wd = Rng.Words(1)
If Asc(Wd) < 65 Then
Inter = 1
Else
Set Rng = Rng.Words(1)
With Rng
' replace Len(Wd) with a fixed number of repeats,
' if you don't want to give a hint about the removed word.
.Text = String(Len(Wd) - 1, "_") & " "
.Collapse wdCollapseEnd
End With
With WordList.Range
If .Words.Count > 1 Then .InsertAfter Chr(11)
.InsertAfter Wd
End With
Inter = CInt(Interval)
End If
Loop
Application.ScreenUpdating = True
End Sub
In order to avoid processing non-words my above code tests, roughly, if the first character is a letter (ASCII > 64). This will preclude numbers and it will allow a lot of symbols. For example "€100" would be accepted for replacement but not "100". You may wish to refine this test, perhaps creating a function like you originally did. Another way I thought of would be to exclude "words" of less than 3 characters length. That would eliminate CrLf (if Word considers that one word) but it would also eliminate a lot of prepositions which you perhaps like while doing nothing about "€100". It's either very simple, the way I did it, or it can be quite complicated.
Variatus - thank you so much for this. It works absolutely perfectly and will be really useful for me.
And your comments are helpful for me to understand some of the commands you use that I am not familiar with.
I'm very grateful for your patience and help.

word vba range not repositioning using a loop

I am working on a macro in word. It pulls cell contents from some cells in an excel doc, puts part of them at the end of the word doc, bolds the first part, then puts the rest of the string and unbolds it.Then it looks for the next match in the excel doc and repeats until there are no matches.
On the second pass through the loop, it continues to affect the content added in the first pass. The font with block also affects the previous line and ends up bolding the entire thing. I set the object to Nothing at the end of the function so I wouldn't expect it to see the first part of the loop as part of the range any longer.
Do
x = AssembleSentence(Last, First, Rank)
Set Loc = .FindNext(Loc)
Loop While Not Loc Is Nothing And Loc.Address <> sFirstFind
Function AssembleSentence(Last, First, Rank)
Dim sText0 As String, sText As String, oText As Object
Set oText = ActiveDocument.Content
sText0 = First & " " & Last
sText = ", " & Rank & " Professor at College of Hard Knocks."
Set oText = ActiveDocument.Content.Paragraphs.Add
oText.Range.SetRange Start:=ActiveDocument.Range.End, End:=ActiveDocument.Range.End
Selection.EndKey Unit:=wdStory
With oText.Range
.InsertAfter (sText0)
With .Font
.Bold = True
End With
End With
Selection.EndKey Unit:=wdStory
With Selection
.Text = sText
With .Font
.Bold = False
End With
End With
Selection.EndKey Unit:=wdStory
Set oText = Nothing
End Function
Still unsure why the loop doesn't redo the range to the end on its own, but this fixes it so that it stops affecting prior looped content.
Looking at my oText.range start/end properties it looks like it is 1034/1035 with a length of 1036 on the first pass and then 1036/1209 with a length of 1210 on the second pass. That is the issue - I don't know why it isn't 1208/1209 on the second pass after setting the object to nothing at the end of the first pass, but the following edit fixes the issue.
With oText.Range
.SetRange Start:=oText.Range.End, End:=oText.Range.End
.InsertAfter (sText0)
With .Font
.Bold = True
End With
End With

Word disappearing text

I am currently setting up some MS Word templates in Word 2010 and have encountered a problem, where text suddenly disappears at the end of a paragraph.
The problem only occurs in some specific scenarios, but I have experienced that it can be recreated in a lot of different ways. I have not, however, been able to pinpoint the exact reason why this happens. Therefore, I would like to find the specific reason, that makes the issue occur, in order to avoid it.
It seems that a combination of the existence of wrapped tables, content in the page header and a certain length of a line can invoke the issue.
To recreate a document where this issue occurs, please follow this procedure:
Open a new document in Word 2010.
Copy the code below into a new module in the VBA editor.
Run the A_ReplicateScenario macro to insert example content in the document.
Place the cursor at the end of line 3 (the line that ends close to the margin).
Type a new sentence after the dot, beginning with a space.
The text that you have typed, will disappear when the margin is reached.
The text will then be shown if for instance a character is deleted from the original text (i.e. from the beginning of the line) or if a formatting change is made (e.g. clear formatting). The 'Show all' setting in Word can also sometimes display the text, but will only display it while 'Show all' is activated. Other times Word will display 'ghosted' double lines which can not be selected.
A short video of the replicated issue can be viewed here: https://youtu.be/Bqp9STDRkXc
Sub A_ReplicateScenario()
Call SetUpNormalStyle
Call InsertBodyTextLines
Call InsertHeaderTextLines
Call InsertWrappedTables
Call SetUpMargins
Call InsertExampleBodyText
End Sub
Sub SetUpNormalStyle()
With ActiveDocument.Styles("Normal").Font
.Name = "Arial"
.Size = 10
End With
With ActiveDocument.Styles("Normal").ParagraphFormat
.SpaceAfter = 0
.LineSpacingRule = wdLineSpaceAtLeast
.LineSpacing = 12
End With
End Sub
Sub InsertBodyTextLines()
For i = 1 To 4
Selection.TypeParagraph
Next
End Sub
Sub InsertHeaderTextLines()
If ActiveWindow.View.SplitSpecial <> wdPaneNone Then
ActiveWindow.Panes(2).Close
End If
If ActiveWindow.ActivePane.View.Type = wdNormalView Or ActiveWindow. _
ActivePane.View.Type = wdOutlineView Then
ActiveWindow.ActivePane.View.Type = wdPrintView
End If
ActiveWindow.ActivePane.View.SeekView = wdSeekCurrentPageHeader
For i = 1 To 26
Selection.TypeParagraph
Next
ActiveWindow.ActivePane.View.SeekView = wdSeekMainDocument
End Sub
Sub InsertWrappedTables()
Selection.HomeKey Unit:=wdStory
ActiveDocument.Tables.Add Range:=Selection.Range, NumRows:=1, NumColumns:=1, DefaultTableBehavior:=wdWord9TableBehavior, AutoFitBehavior:=wdAutoFitFixed
With Selection.Tables(1).Rows
.WrapAroundText = True
.HorizontalPosition = CentimetersToPoints(2)
.RelativeHorizontalPosition = wdRelativeHorizontalPositionPage
.VerticalPosition = CentimetersToPoints(4.5)
.RelativeVerticalPosition = wdRelativeVerticalPositionPage
End With
Selection.Tables(1).Columns(1).PreferredWidthType = wdPreferredWidthPoints
Selection.Tables(1).Columns(1).PreferredWidth = CentimetersToPoints(11)
Selection.MoveDown Unit:=wdLine, Count:=1
ActiveDocument.Tables.Add Range:=Selection.Range, NumRows:=1, NumColumns:=1, DefaultTableBehavior:=wdWord9TableBehavior, AutoFitBehavior:=wdAutoFitFixed
With Selection.Tables(1).Rows
.WrapAroundText = True
.HorizontalPosition = CentimetersToPoints(10)
.RelativeHorizontalPosition = wdRelativeHorizontalPositionPage
.VerticalPosition = CentimetersToPoints(8)
.RelativeVerticalPosition = wdRelativeVerticalPositionPage
End With
Selection.Tables(1).Columns(1).PreferredWidthType = wdPreferredWidthPoints
Selection.Tables(1).Columns(1).PreferredWidth = CentimetersToPoints(9)
End Sub
Sub SetUpMargins()
With ActiveDocument.PageSetup
.TopMargin = CentimetersToPoints(3.8)
.BottomMargin = CentimetersToPoints(2.8)
.LeftMargin = CentimetersToPoints(2.3)
.RightMargin = CentimetersToPoints(1.5)
End With
End Sub
Sub InsertExampleBodyText()
With Selection
.HomeKey Unit:=wdStory
.MoveDown Unit:=wdLine, Count:=3
.TypeText Text:="Ouwouwouwoiwoiuwoiuwoiuwoiuwoiuwoiuwoiw oiwu oiwu owiu woiu woiuw oiwu owiu owiu ww."
.TypeParagraph
.TypeText Text:="Woiuwoiuwoiuw."
End With
End Sub
The problem is related to the tables being formatted to float around the text. Word has a long history of issues with floating objects. And although Word has improved a lot over the years you might still experience problems, in particular with floating tables.
If you change the formatting of the second table (via Table Properties) and set the text wrapping to None, the bug goes away (YMMV).
My recommendation would be to avoid the floating tables if possible.

How do I strip all formatting out of this Word VBA output and use the "Normal" quickstyle?

I am using the following VBA macro to add page numbers after all bookmark hyperlinks in my document:
Sub InsertPageRefs()
Application.ScreenUpdating = False
Dim hLnk As Hyperlink, Rng As Range
For Each hLnk In ActiveDocument.Hyperlinks
With hLnk
If InStr(.SubAddress, "_Toc") = 0 And .Address = "" Then
Set Rng = .Range
With Rng
.Collapse Direction:=wdCollapseEnd
.InsertAfter Text:=" (See page #)"
.Font.Underline = wdUnderlineNone
End With
ActiveDocument.Fields.Add Range:=Rng.Characters(InStr(Rng, "#")), Text:="PAGEREF " & .SubAddress
End If
End With
Next
Set Rng = Nothing
Application.ScreenUpdating = True
Application.ScreenRefresh
MsgBox ActiveDocument.Hyperlinks.Count & " page numbers have been added.", vbOKOnly
End Sub
However, it's having undesirable results.
The blue color of the hyperlinks is partially spilling over into the added text.
It's creating a bunch of crazy span tags when I save the resulting file to HTML. I don't want this because I am going to convert the HTML to .mobi for Kindle and all the span tags are going to create chaos in my .mobi.
How do I strip out all the formatting and insert the page numbers in the "Normal" word style?
I suspect the real answer for this would be to use a good e-book editor that will keep track of this for you.
That said, the problem is likely that you are working on the Hyperlink's range, so all you should have to do is duplicate it. This allows the formatting of your range separate itself from whatever formatting is attached to the hyperlink. The other benefit of using a duplicate of a Hyperlink's range is that you can operate on the text of the range directly without destroying the link, which is also an easy way to preserve the target formatting:
Sub InsertPageRefs()
Dim hLnk As Hyperlink
Dim Rng As Range
For Each hLnk In ActiveDocument.Hyperlinks
If InStr(hLnk.SubAddress, "_Toc") = 0 And hLnk.Address = vbNullString Then
Set Rng = hLnk.Range.Duplicate
Rng.Start = Rng.End
Rng.Text = " (See page #)"
Rng.Font.Underline = wdUnderlineNone
ActiveDocument.Fields.Add Range:=Rng.Characters(InStr(Rng, "#")), _
Text:="PAGEREF " & hLnk.SubAddress
End If
Next
MsgBox ActiveDocument.Hyperlinks.Count & " page numbers have been added.", vbOKOnly
End Sub
Note that I pulled out the With blocks to make this more readable. Nested Withs make it a lot more difficult to tell at a glance what object you're operating on.

How to know the word under the right click in Word

I have VBA for Word that adds a button to the context menu of the right click which launches my application (which works).
I need the word clicked on to pass it as argument. I saw that I couldn't use Selection because right click doesn't select the word, it gives me the letter after the cursor.
With what I've read, I could possibly look at the position of the cursor, then look at both sides to where the word begins and finishes.
This seems to work
Selection.Words(1).Text
Edit
A little more robust to account for ends of sentences.
Sub FindWord()
Dim rWord As Range
If Selection.Words(1).Text = vbCr Then 'end of sentence
'get last word of sentence
Set rWord = Selection.Words(1).Previous(wdWord)
Else
'get selected word
Set rWord = Selection.Words(1)
End If
'There has to be a better way than this
If rWord.Text = "." Or rWord.Text = "?" Then
Set rWord = rWord.Previous(wdWord)
End If
Debug.Print rWord.Text
End Sub
Here is the most simple way to check for the word under the cursor.
Sub Sample()
Dim pos As Long
'~~> if the cursor is at the end of the word
Selection.MoveEnd Unit:=wdCharacter, Count:=1
Do While Len(Trim(Selection.Text)) = 0
'~~> Move one character behind so that the cursor is
'~~> at the begining or in the middle
Selection.MoveEnd Unit:=wdCharacter, Count:=-1
Loop
'~~> Expand to get the word
Selection.Expand Unit:=wdWord
'~~> Display the word
Debug.Print Selection.Text
End Sub