I wrote the 1st ever macro.(Windows 10, WORD 2019)
I'm trying to find words that start with a capital letter in Standard style and continue to have Italic style.
Unfortunately, if I search a document using too many letters in the query - the macro closes (after checking, for example, 1.5 pages) or resets the WORD program.
If I reduce the number of words searched - the macro starts to run longer and longer.
With, for example, a search for 1 letter (U) instead of 32 (ABCDEFGHIJKLŁMNOPQRSTUVWXYZĆŚŃŻŹ) - it does not crash the program.
I tried to add
Application.ScreenUpdating = False at the beginning and
Application.ScreenUpdating = True at the end of the code but it doesn't help much.
Sub Makro1()
Dim Rng As Range
Selection.MoveRight Unit:=wdCharacter, Count:=2
Selection.find.ClearFormatting
With Selection.find.Font
.Bold = False
.Italic = False
End With
Selection.find.Replacement.ClearFormatting
With Selection.find
.Text = "U"
'.Text = "([ABCDEFGHI])"
'.Text = "([ABCDEFGHI])"
'.Text = "([JKLŁMNOP])"
'.Text = "([QRSTUVWX])"
'.Text = "([YZĆŚŃŻŹ])"
'.Text = "([ABCDEFGHIJKLŁMNOPQRSTUVWXYZĆŚŃŻŹ])"
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindContinue
.Format = True
.MatchCase = False
.MatchWholeWord = False
.MatchAllWordForms = False
.MatchSoundsLike = False
.MatchWildcards = True
End With
Selection.find.Execute
Selection.MoveRight Unit:=wdCharacter, Count:=2
Set Rng = Selection.Range
If Rng.Italic = True Then
MsgBox "FIND"
End
If Rng.Italic = False Then
Call Makro1
End If
End If
Call Makro1
End Sub
Thanks but your code didn't work. I change the code to:
If .Words.Last.Characters(2).Font.Italic = True Then
.Characters(1).Font.Italic = True
End If
.find.Execute
but it only worked sometimes. When I enabled the macro on a longer text, the macro would hang, show error 5941 "collection object not exist" and stop on the line:
If .Words.Last.Characters(2).Font.Italic = True Then
I spent a lot of time before I discovered why. It turns out that the macro crashes because sometimes there is a punctuation mark in the text next to a capital letter, e.g. period comma...etc. e.g A. B; C: D., The code would have to be improved to ignore this. I try add .IgnorePunct = True after .MatchCase = True but didn't help and macro still crashes
I try change code:
.Text = "<[A-Z][!.,;:„”#$%-+=[\[/[//[#[?[![#[$[%[*[([)[&[*[{[}]*>"
Macro fix some errors in text, work longer but still somewhere is an unacceptable sign after a character with a capital letter and the macro crashes.
Add more unwanted signs
.Text = "<[A-Z][!.,;:„”#$%-+=[\[/[//[#[?[![#[$[%[*[([)[&[*[{[}[<[>_|` - …]*>"
and still the macro crashes.
After rewriting the code to show live which words it checks, I was able to understand in part why the macro crashes.
Problem is hard space:
1 and the comma after digit
if I use normal space instead of hard space - macro work OK.
If change to N = 5, to N = 5 , also work OK.
Another place with STOP working:
2
3
I don't know what other adjustments to make so that the macro doesn't hang up in these places.
[EDIT]
I don't know too much what I'm doing, but I changed the code to:
Sub m1select()
Application.ScreenUpdating = False
With Selection.find
.Text = "<[A-Za-z0-9][! ^13.^s^t^+^=,;:„”#$%-+=[\[[\][\\[\/[\//[\#[\?[\![\#[\$[\%[\*[\([\)[\&[\*[\{[\}[\<[\>_|` - …-]*>"
.MatchWildcards = True
.Execute Forward:=True, Wrap:=wdFindStop
End With
With ActiveDocument.Content
Do While Selection.find.Found
If Selection.Words.Last.Characters(1).Font.Italic = True Or Selection.Words.Last.Characters(2).Font.Italic = True Then
Selection.Font.Italic = True
End If
Selection.find.Execute
Loop
End With
Application.ScreenUpdating = True
End Sub
and it works. It now corrects all words with partial Italic font not just capitalized.
Admittedly, it does it slowly because I used "Select" , but that way I can see what character the macro hangs on and add it to the exclusion in after the [!
( as all the time is Application.ScreenUpdating = True)
Doesn't work on expressions with a single U e.g.
U letter (after correction it should be U letter) but I make it myself with another script.
You guided me to the solution.
The problem was calling the macro in a loop using Call.
After adding START: at the beginning (and then the rest of the code) and instead of "Call Macro 1" writing "GoTo START" in two places solved the problem.
you're not finishing your code correctly.
When calling a macro from that macro you're creating a loop which never ends. By calling macro1 all the time, the runtime will terminate the Word Application after too many iterations.
You probably don't need to loop (which is a wired loop you created there) through the selection.
The Find-Object provides everything you need. To write all capital letters which are the first ones in a word to italic, when it's not bold or underlined, you could use this, for example:
Sub m()
With ActiveDocument.Content.Find
.Text = "<[A-Z]"
.Style = "Standard"
.Font.Bold = False
.Font.Underline = False
.MatchWildcards = True
.MatchCase = True
.Replacement.Font.Italic = True
.Execute Replace:=wdReplaceAll, Forward:=True, Wrap:=wdFindContinue
End With
End Sub
If you want to set the first capital character of a word to italic, when the secnd character is italic too, you have to loop over all possible words:
Sub m1()
With ActiveDocument.Content
With .Find
.Text = "<[A-Z]*>"
.MatchWildcards = True
.MatchCase = True
.Execute Forward:=True, Wrap:=wdFindStop
End With
Do While .Find.Found
If .Words.Last.Characters(2).Font.Italic = True Then .Font.Italic = True
.Find.Execute
Loop
End With
End Sub
You could specify other letters like you did above if you need/want.
If you need more precise selection criteria, you can search them on the help page:
https://learn.microsoft.com/en/office/vba/api/word.find
Good Luck
Related
I have a question about a problem i need do solve.
I have a word doc, on which i want to run a search and replace query.
But i dont want to change the actual text, i want to put the changed text
in the clipboard and leave the actual one unchanged.
Sub SearchAndReplace()
' marks all bold words,italic words, underlined
Selection.Find.ClearFormatting
Selection.Find.Font.Bold = True
Selection.Find.Font.Italic = True
Selection.Find.Font.Underline = wdUnderlineSingle
Selection.Find.Replacement.ClearFormatting
With Selection.Find
.Text = ""
.Replacement.Text = "[test]^&[/test]"
.Forward = True
.Wrap = wdFindContinue
.Format = True
.MatchCase = False
.MatchWholeWord = True
.MatchAllWordForms = False
.MatchSoundsLike = False
.MatchWildcards = True
End With
Selection.Find.Execute Replace:=wdReplaceAll
End Sub
Cautious:
I guess there are better ways to achieve what you want to do as this is not proper coding. (Also as the clipboard only has a limit of 24 items), but for the sake of making your code works, here is my answer:
To copy the text in the clipboard instead of replacing it, you can just use the .copy method.
If you replace your line:
Selection.Find.Execute Replace:=wdReplaceAll
by
Dim iCount As Long
Selection.Find.Execute
Selection.Copy
Do While Selection.Find.Found = True And iCount < 1000
iCount = iCount + 1
Selection.Find.Execute
Selection.Copy
Loop
it should work.
The code is simply copying the item found after each search.
Selection.Find.Execute searches the next itteration of the search every time it appears in the code.
Once there are no search found anymore (or there are too many searches), the loop will stop.
I am trying to find a word and save the selection in an array and then find again and then save the next selection in the array. And in the end try to select all the selections in the array.
I am trying this but its with half knowledge. I am not able to get it. Can some one help.
Sub Macro6()
'
' Macro6 Macro
'
'
Selection.HomeKey Unit:=wdStory
Dim selecttest(2) As Selection
For I = 1 To 2
Selection.Find.ClearFormatting
With Selection.Find
.Text = "PQXY"
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindContinue
.Format = True
End With
Selection.Find.Execute
Set selecttest(I) = Selection
Selection.MoveRight Unit:=wdCharacter, Count:=1
Next I
For I = 1 To 2
selecttest(I).Select
Next I
End Sub
I want to keep the selection in the loop and show them in the end.
Solution i tried:
Sub Macro61()
'
' Macro6 Macro
'
'
Selection.HomeKey Unit:=wdStory
Dim selecttest(2) As Range
For i = 1 To 2
Selection.Find.ClearFormatting
With Selection.Find
.Text = "PQXY"
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindContinue
.Format = True
End With
Selection.Find.Execute
Set selecttest(i) = Selection.Range
Selection.MoveRight Unit:=wdCharacter, Count:=1
Next i
For i = 1 To 2
selecttest(i).Select
Next i
End Sub
Problem above is selecttest(2) is only selected. I want the end result with both selecttest(1) and selecttest(2) selected
I also tried
Dim totalselect as Range
For i = 1 to 2
set totalselect = totalselect + selectest(i)
Next i
totalselect.select
It shows error that "+" (plus) operation does not exist
Solution: Not Possible
Found some articles regarding discontinous range selections is not possible by VBA whereas its possible by FindAll
Find All in VBA: https://forums.windowssecrets.com/showthread.php/124485-Find-All-in-VBA
Reason 1: which talks about findall
Unfortunately, Microsoft omitted to add support for "Find All" in the
VBA object model for Word. In other words, Find All cannot be executed
from a macro.
You can loop through all occurrences of the search text in VBA, but
that's not the same as Find All.
Reason 2: findall is inderectly related to discontiguous selections which is not possible
Probably the reason a Find All isn't in VBA is that VBA also has never
had any way to deal with discontiguous selections (the kind you can
make with Ctrl and the mouse), which is what Find All would produce.
The KB article here explains the few things that can be done. Every
version since 2002 (including 2010) has made no changes in this area.
Reason 3: computationally expensive, in terms of both processing and memory. if done by VBA
I suspect this omission was intentional and carefully considered. In
the visual context of an open document window, Find All is a perfectly
sensible concept. However, in the procedural world of VBA, it is a tad
more difficult to work with sets of things, and, often,
computationally less efficient.
This isn't to say that it can't be done in VBA, only that I can
understand why it wasn't done. For what it's worth, the same is true
of the Find object in Excel, with which I have much more intimate, and
recent, experience.
To support Find All in VBA would require the Execute method to return
a collection of Range objects, which could be computationally
expensive, in terms of both processing and memory
Try with two simple changes replacing Selection into Range object:
Sub Macro6()
...
...
Dim selecttest(2) As Range 'not Selection
...
...
Set selecttest(i) = Selection.Range 'not just a Selection
...
End sub
If all you want to do is show the matches, you only need:
ActiveDocument.Range.Find.HitHighlight FindText:="PQXY"
Dim oRng As Word.Range
Set oRng = Selection.Range
oRng.Find.ClearFormatting
With oRng.Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = "shhada"
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchKashida = False
.MatchDiacritics = False
.MatchAlefHamza = False
.MatchControl = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
While .Execute
oRng.Editors.Add wdEditorEveryone
Wend
ActiveDocument.SelectAllEditableRanges wdEditorEveryone
ActiveDocument.DeleteAllEditableRanges wdEditorEveryone
End With
End Sub
i have many documents with variable-words inside brackets like this:
[my_word]
My code already finds all those words and i save them as String.
Now I need a function to replace this String with a ContentControl Element. Is this possible? Because I first need to generate an element, then change the text inside and the tag of it, both with [my_word].
Any help is appreciated.
My Code so far looks like this (right now it is possible to replace one word at a time with a control element; I would like to replace all inside on macro. Word tells me it is not possible to replace multiple Selections, so I would have to rerun this macro mutliple times manually..)
Sub ReplaceTags()
'
' ReplaceTags Macro
'
'
Selection.Find.ClearFormatting
With Selection.Find
.Text = "\<?*\>"
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindContinue
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchAllWordForms = False
.MatchSoundsLike = False
.MatchWildcards = True
End With
Selection.Find.Execute
Selection.Range.ContentControls.Add (wdContentControlText)
End Sub
EDIT:
I worked on my code a little bit and found a solution to replace the text I am looking for:
Sub ReplaceTags()
'
' ReplaceTags Macro
'
'
With Selection.Find
.ClearFormatting
.Text = "\<?*\>"
.Execute Forward:=True
.MatchWildcards = True
End With
If Selection.Find.Found = True Then
Selection.Range.ContentControls.Add (wdContentControlText)
Selection.ParentContentControl.Tag = Selection.Text
End If
End Sub
Still I am not quite sure how to do this on the complete document without having to click "run macro" a whole lot of times
EDIT(EDIT):
I solved it. In case somebody has the same problem in future:
Sub ReplaceAllTags()
'
' ReplaceAllTags Macro
'
'
For i = 0 To ActiveDocument.Words.Count
Selection.EscapeKey
Application.Run MacroName:="ReplaceTags"
Next
End Sub
ReplaceTags() is the same function as above.
I have a Word document which contains of lot of styles; in that I want to select particular style and make starting numbers alone bold in all the paragraph style
eg:
1. first numbers alone
23. first number alone
This is my code
Sub ParaStyle()
Selection.HomeKey wdStory
Dim i As Integer
i = 1
Do Until i = Application.ActiveDocument.Paragraphs.Count
If Selection.ParagraphFormat.Style = "heading3" Then
Selection.Find.ClearFormatting
Selection.Find.Style = ActiveDocument.Styles("heading3")
Selection.Find.Replacement.ClearFormatting
Selection.Find.Replacement.Font.Bold = True
With Selection.Find
.Text = "([0-9]{1,2})"
.Replacement.Text = "\1"
'.Forward = True
.Wrap = wdFindContinue
.Format = True
.MatchWildcards = True
.Execute Replace:=wdReplaceOne
End With
End If
i = i + 1
Loop
End Sub
Thanks in advance
You are running the search on the Selection, but you're not changing that selection between runs. So you just end up making the same text bold over and over again. Here's a way to do what you're doing without the Selection object:
Sub ParaStyle()
Dim objPara As Paragraph
For Each objPara In ActiveDocument.Paragraphs
If objPara.Style = "heading3" Then
With objPara.Range.find
.ClearFormatting
.Text = "([0-9]{1,2})"
.Style = ActiveDocument.Styles("heading3")
With .Replacement
.ClearFormatting
.Font.Bold = True
.Text = "\1"
End With
'.Forward = True
.Wrap = wdFindContinue
.Format = True
.MatchWildcards = True
.Execute replace:=wdReplaceOne
End With
End If
Next objPara
End Sub
I didn't change much; instead of using a Do loop we loop through all the paragraphs in the document and work on each one. You can also use your code but make sure to do ActiveDocument.Paragraphs(i).Select before running the replace. I don't recommend that, as it's best to avoid using the Selection object when you can (one good reason to avoid it is that, if you have a script that takes a while and you try to do something else in a text editor, say, you'll run the risk of contaminating your clipboard).
Do keep in mind that there's nothing here to prevent this search from finding a number in the middle of the paragraph if there's none at the beginning. I'm assuming that narrowing it down by style is enough for you, or you wouldn't be using this approach.
this is my first question,
I have to go over a large number of documents and make sure that several formatting issues are correct. An example of such an issue would be to make sure that all periods, ".", are not bold, italics, underline, etc. Another example would be to make sure that all "etc." are in italics.
I have a list of the needed formatting issues.
Instead of going over each document and using the find/replace function I would rather write a macro that I can apply to each document.
I have no experience with VBA. I do on the other hand, have some experience with programming in C sharp and C in general.
Any help would be greatly appreciated.
BTW, I'm not asking for a complete program, rather a sample from which I can learn and continue with my own.
There are a couple options:
1.Under the Developer tab in Word, you can hit the "Record Macro" button and do a find and replace multiple times while recording the macro using the ctrl + H shortcut.
2.Have multiple smaller macros setup (such as the two below) hit the "Record Macro" and run them in the order that you want.
Sub ItalicizeEct()
Selection.Find.ClearFormatting
Selection.Find.Replacement.ClearFormatting
Selection.Find.Replacement.Font.Italic = True
With Selection.Find
.Text = "ect."
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindContinue
.Format = True
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
End With
Selection.Find.Execute Replace:=wdReplaceAll
End Sub
Sub RemoveBoldPeriods()
Dim PunctAllRng As Word.Range
Set PunctAllRng = ActiveDocument.Range
With PunctAllRng.Find
.Format = True
.Text = "."
.Font.Bold = True
.Replacement.Text = "."
.Replacement.Font.Bold = False
.Execute Replace:=wdReplaceAll
End With
End Sub
3.Or simply write a large macro that will go through all the editing processes you need