MS Word VBA: find a textbox by location

MS Word VBA: find a textbox by location:

I have a document with between 4 to 12 textboxes. Using VBA I find the textboxes with specific text in them. Now I need to find the textbox immediately before that textbox by position.  I thought a "For Each varShape In ActiveDocument.Shapes" loop would find the textboxes in order that they occur in the document (I use varShape.Anchor.start to find the character position of the anchor) but it does not. Currently I am thinking of saving the name of each textbox and its position in an array, then sort the array by position. But I wonder if there is a way to directly get the textbox that is positioned before the textbox I found?

Thanks
LVL 39
thenelsonAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

GrahamSkanRetiredCommented:
Nelson,

You might try locating the top left hand corners of the shapes using the Top and Left properties, but...

I had prepared some VBA to try to answer your previous question before I saw that you had requested its deletion. I was about to give you some general advice about the sample document that you posted. Boiled down that advice was. "In any maintenance procedure on this document, don't start from here".
0
GrahamSkanRetiredCommented:
Hmm. That wasn't easy.
I have produced this:
Option Explicit

Type Box
    strType As String
    iSizeGroup As Integer
    iRow As Integer
    iColumn As Integer
    strName As String
End Type

Sub FindBoxes5()
Dim Boxes() As Box
Dim varShape As Shape
Dim b As Integer
'Size 1 Height ~ 330 points
'Size 2 Height ~ 117 points
'Size 3 Height ~ 9.3 points
'Size 4 Height ~ 7.3 points
Debug.Print "Left", "Top", "Height", "Width", "Name"
For Each varShape In ActiveDocument.Shapes
    With varShape
    Debug.Print .Left, .Top, .Height, .Width, .Name
        ReDim Preserve Boxes(b)
        Select Case .Type
            Case 9
                Boxes(b).strType = "Line"
            Case 13
                Boxes(b).strType = "Picture"
            Case 17
                Boxes(b).strType = "Text Box"
        End Select
        If Abs(.Height) - 7.3 < 2 Then
            Boxes(b).iSizeGroup = 4
        ElseIf Abs(.Height) - 9.3 < 2 Then
            Boxes(b).iSizeGroup = 3
        ElseIf Abs(.Height) - 117 < 2 Then
            Boxes(b).iSizeGroup = 2
        ElseIf Abs(.Height) - 362 < 5 Then
            Boxes(b).iSizeGroup = 1
        End If
        If .Top < 350 Then
            Boxes(b).iRow = 1
        Else
            Boxes(b).iRow = 2
        End If
        If .Left < 300 Then
            Boxes(b).iColumn = 1
        Else
            Boxes(b).iColumn = 2
        End If
        Boxes(b).strName = .Name
        b = b + 1
    End With
Next varShape
Debug.Print
Debug.Print "Type", "SizeGroup", "Row", "Column", "Name"

For b = 0 To UBound(Boxes)
    With Boxes(b)
        Debug.Print .strType, .iSizeGroup, .iRow, .iColumn, .strName
    End With
Next b
End Sub

Open in new window

0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
thenelsonAuthor Commented:
Graham,

I looked at using the top and left attributes of the textboxes but they are anchored to the associated text so those attributes are not meaningful for the absolute position.

I think you are right with "I suggest that the document be laid out as a two-column table. Text boxes that take the whole width could be cells." However in this document I have one textbox in each form that cannot go into a table - it acts like a security watermark.
0
thenelsonAuthor Commented:
Graham,

What I came up with is using Anchor.start which returns the number of characters from the beginning of the document. The character count snakes through the columns.  I then sort the textboxes by Anchor.start so the textboxes are listed in the order they appear in the document.  I used a one dimension array with comma delineated fields to make the sorting easier.  Once they are sorted, it is easy to hide the correct textboxes. Here is the code:
Sub FindBlankScripts()
Dim varShape As Shape
Dim StartPos As Long, EndPos As Long

intTextboxCount = 0

Debug.Print "Location, Textbox name, Empty, Bookmark name"
For Each varShape In ActiveDocument.Shapes
    If varShape.Type = msoTextBox Then
        'find the large textboxes:
        If InStr(varShape.TextFrame.TextRange.Text, "Jane Barnwell, MD") > 0 Then
            aryTextBox(intTextboxCount) = Format(varShape.Anchor.start, "00000") & "," & varShape.Name
        ElseIf varShape.Height < 340 Then
            'find the empty small textboxes:
            aryTextBox(intTextboxCount) = Format(varShape.Anchor.start, "00000") & "," & varShape.Name
            If Len(varShape.TextFrame.TextRange.Text) < 2 Then
                aryTextBox(intTextboxCount) = aryTextBox(intTextboxCount) & ",empty"
            Else
                aryTextBox(intTextboxCount) = aryTextBox(intTextboxCount) & ","
            End If
            
            'set bookmark for text:
            varShape.Anchor.Select
            Selection.MoveLeft wdCharacter, 1
            With Selection.Find
                .ClearFormatting
                .Text = "Jane Barnwell, MD"
                .Forward = False
                .MatchCase = True
            Selection.Find.Execute
            Selection.HomeKey wdLine
            StartPos = Selection.start
                .Text = "V#:"
                .Forward = True
            End With
            Selection.Find.Execute
            Selection.EndKey wdLine
            EndPos = Selection.start
            ActiveDocument.Range(StartPos, EndPos).Select
            ActiveDocument.Bookmarks.Add "Text" & intTextboxCount
            aryTextBox(intTextboxCount) = aryTextBox(intTextboxCount) & ",Text" & intTextboxCount
        End If
        Debug.Print aryTextBox(intTextboxCount), varShape.Left
        intTextboxCount = intTextboxCount + 1
    End If
Next varShape
Selection.HomeKey wdStory
BubbleSort
End Sub

Sub BubbleSort() '(aryTextBox())

'   Sorts an array using bubble sort algorithm
Debug.Print
    Dim First As Integer, Last As Long
    Dim i As Long, j As Long
    Dim Temp As String
    
    First = LBound(aryTextBox)
    Last = intTextboxCount - 1
    For i = First To Last
        For j = i + 1 To Last
            If Val(aryTextBox(i)) > Val(aryTextBox(j)) Then
                Temp = aryTextBox(j)
                aryTextBox(j) = aryTextBox(i)
                aryTextBox(i) = Temp
            End If
        Next j
    Next i
    For i = 0 To intTextboxCount - 1
        Debug.Print aryTextBox(i)
    Next i
End Sub

Sub HideBlankScripts(Optional booHide = False)
Dim i As Integer

For i = 0 To intTextboxCount - 1
    If InStr(aryTextBox(i), "empty") Then
        ActiveDocument.Shapes(Mid(aryTextBox(i - 1), 7, 11)).Visible = Not booHide
        ActiveDocument.Bookmarks(Mid(aryTextBox(i), 25)).Range.Font.Hidden = booHide
    End If
Next i
End Sub

Open in new window

This is what the code outputs:
before sort:
Location, Textbox name, Empty, Bookmark name
00000,Text Box 21
00579,Text Box 23
01157,Text Box 24
01737,Text Box 25
00227,Text Box 40,empty,Text4
00807,Text Box 42,empty,Text5
01965,Text Box 43,empty,Text6
01385,Text Box 44,empty,Text7

after sort:
00000,Text Box 21
00227,Text Box 40,empty,Text4
00579,Text Box 23
00807,Text Box 42,empty,Text5
01157,Text Box 24
01385,Text Box 44,empty,Text7
01737,Text Box 25
01965,Text Box 43,empty,Text6


Thanks for your help!!
0
GrahamSkanRetiredCommented:
I am pleased to learn that the anchor ranges are sufficiently related to the textbox positions for your purpose in this document. This is usually the case, but it is not a definite rule. A shape near the bottom of the page can have its anchor near the top, and vice-versa.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
VBA

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.