Reputation: 13
I've created a small macro for inserting a hidden bookmark to a numbered paragraph
Sub blablabla()
Dim BkmrkName As String
ActiveDocument.Bookmarks.ShowHidden = True
Application.ScreenUpdating = False
heanum = InputBox("Enter Heading1 number", "List paragraph", "1")
Select Case Len(heanum)
Case 1
sPos1 = "00" & Left(heanum, 1)
Case 2
sPos1 = "0" & Left(heanum, 2)
Case 3
sPos1 = Left(heanum, 3)
End Select
ActiveDocument.ConvertNumbersToText
lisnum = Left(Selection, InStr(Selection, vbTab))
ActiveDocument.Undo
If IsNumeric(Left(lisnum, 1)) = True Then
lisnum = Left(lisnum, Len(lisnum) - 2)
Select Case Len(lisnum)
Case 3
sPos2 = "00" & Right(lisnum, 1)
Case 4
If Mid(lisnum, 2, 1) = Chr(46) Then
sPos2 = "0" & Right(lisnum, 2)
ElseIf Mid(lisnum, 3, 1) = Chr(46) Then
sPos2 = "00" & Right(lisnum, 1)
End If
Case 5
If Mid(lisnum, 2, 1) = Chr(46) Then
sPos2 = Right(lisnum, 3)
ElseIf Mid(lisnum, 3, 1) = Chr(46) Then
sPos2 = "0" & Right(lisnum, 2)
ElseIf Mid(lisnum, 4, 1) = Chr(46) Then
sPos2 = "00" & Right(lisnum, 1)
End If
Case 6
If Mid(lisnum, 3, 1) = Chr(46) Then
sPos2 = Right(lisnum, 3)
ElseIf Mid(lisnum, 4, 1) = Chr(46) Then
sPos2 = "0" & Right(lisnum, 2)
End If
Case 7
sPos2 = Right(lisnum, 3)
End Select
End If
ActiveDocument.Bookmarks.Add Name:=Chr(95) & sPos1 & Chr(95) & sPos2
Application.ScreenUpdating = True
End Sub
The user select a numbered paragraph and triggers the macro. Macro runs once per trigger and inserts a hidden bookmark with the name like _001_042
if the selection begins with "any_character".42. The first "001" is meant to depict a chapter number (i.e. "Chapter 1"), but could be any number and is determined by a user input through a message box. This macro works, though with each next numbered paragraph runs slower and slower. When I get to paragraph 1.100 it takes ~5 minutes (!!!) for macro to insert a single bookmark "_001_100".
Why does such a long latency happen? Is it possible to optimize the macro to run faster?
Many thanks in advance!
Upvotes: 0
Views: 259
Reputation: 13
This macro does it's job in only few seconds:
Sub AddBkmrkSmart()
Dim Author, Year As String
ActiveDocument.Bookmarks.ShowHidden = True
heanum = InputBox("Enter Heading1 number", "Heading1", "1")
Select Case Len(heanum)
Case 1
sPos1 = "H00" & Left(heanum, 1)
Case 2
sPos1 = "H0" & Left(heanum, 2)
Case 3
sPos1 = "H" & Left(heanum, 3)
End Select
'Debug.Print "sPos1: " sPos1
lisnum = Selection.Range.ListFormat.ListValue
'Debug.Print "Iteration 1 lisnum: " & lisnum
Select Case Len(lisnum)
Case 1
sPos2 = "L00" & Left(lisnum, 1)
Case 2
sPos2 = "L0" & Left(lisnum, 2)
Case 3
sPos2 = "L" & Left(lisnum, 3)
End Select
'Debug.Print "sPos2: " sPos2
ActiveDocument.Bookmarks.Add Name:= "_" & sPos1 & "_" & sPos2
End Sub
I suggest is as answer along with Tomalak's one.
Upvotes: 0
Reputation: 338228
Avoid making useless changes to a document. That applies to manual editing, and it applies doubly to VBA code.
Your ConvertNumbersToText
/ Undo
is as close to completely useless as it gets. Don't do such things. Word has an Undo buffer that you stress for no reason with this pointless edit. Despite being a wasteful no-op, you also destroy the user's ability to undo their own actions with this.
First off, you solve the problem of padding a string in the worst possible way, (multiple times!). Lets fix that.
Function PadLeft(ByVal value As String, length As Integer, Optional padding As String = " ")
PadLeft = String(Max(0, length - Len(value)), padding) & value
End Function
This function will pad any string to any given length. However, it depends on another utility function that will return the greater of two numbers.
Function Max(a As Long, b As Long) As Long
If b > a Then Max = b Else Max = a
End Function
Now, how about this code:
Sub SetParagraphBookmark()
Dim para As Range, _
paraNum As Long, headerNum As String, _
prefix As String, suffix As String
Set para = Selection.Paragraphs(1).Range
paraNum = para.ListFormat.ListValue
If paraNum Then
headerNum = InputBox("Enter Heading1 number", "List paragraph", "1")
If headerNum > "" Then ' otherwise the user clicked Cancel
prefix = PadLeft(headerNum, 3, "0")
suffix = PadLeft(paraNum, 3, "0")
ActiveDocument.Bookmarks.Add "_" & prefix & "_" & suffix, para
End If
Else
MsgBox "Please click on a valid list paragraph first.", vbInformation
End If
End Sub
This sets a bookmark that spans the entire paragraph the cursor is in, without moving the cursor or making any other changes to the document.
General notes:
Chr(46)
instead of "."
?PadLeft
and Max
. This allows re-using bits of your code elsewhere. You might want to place them into a separate Utilities module as well.ConvertNumbersToText
. Taking some time to pick the right property from the right object pays. There will be a lot documentation-reading involved, you'll just have to deal with that. Luckily the Microsoft documentation is superb.Option Explicit
at the top of your modules. There is a setting in the VBA IDE's options for that ("Require variable declaration"). Enable it. Manually add that line to any module that does not have it. Fix the errors you get before you do anything else. (Disable the "Auto syntax check" feature while you are at it, this feature is counter-productive.)Upvotes: 2