PDA

View Full Version : [SOLVED:] Finding Shapes in a document



smallken
09-10-2012, 05:13 PM
I want to find the next shape or textbox in a document following on from the current selection. I want the order in the document not the order in the Shapes collection. I am using the code:



If Selection.ShapeRange.Count > 0 Then ' selection is inside a shape
Selection.ShapeRange(1).Anchor.Paragraphs(1).Range.Select
Selection.MoveDown unit:=wdLine ' move down one line from anchor
End If
Set pRange = Selection.Range
pRange.End = ActiveDocument.Content.End
If pRange.ShapeRange.Count >0 Then
For Each sHp In pRange.ShapeRange
Exit For
Next sHp
End if

The problem with this code is that if there are several shapes anchored to the same paragraph mark then only the first of these is selected and the others are passed over.

Can anyone solve my problem?

Frosty
09-11-2012, 08:37 AM
Why not just nest your shapes loop in a for each paragraph loop.

Note, this may be a slow way to do what you're doing, but since you haven't provided any specifics, here is a generic answer. This will only go through the main body of the document (no shapes in headers, footers, etc).



Sub Demo()
Dim oPara As Paragraph
Dim oShp As Shape
For Each oPara In ActiveDocument
For Each oShp In oPara.Range.ShapeRange
'do whatever you want here
Debug.Print oShp.Name
Next
Next
End Sub

smallken
09-12-2012, 01:39 PM
My requirement is to go to the next shape from a current cursor position or from a current shape selection. The issue is that the current shape may be anchored from a paragraph character that also anchors other shapes so it is not simply a matter of advancing to the next paragraph.

The solution that I currently have is if the current selection is a shape, check its anchor character for multiple shapes, find the current shape's name and advance by one shape. If the current shape is the last in the group then advance to the next paragraph. The resultant code is:



If Selection.ShapeRange.Count > 0 Then ' selection is inside a shape
shName = Selection.ShapeRange(1).Name ' store name of current shape
Set aRange = Selection.ShapeRange(1).Anchor.Paragraphs(1).Range
k = aRange.ShapeRange.Count
i = 1
Do While shName <> aRange.ShapeRange(i).Name And i < k
i = i + 1
Loop
i = i + 1
' if i>k then current shape is last in anchored group
If i <= k Then
Set sHp = aRange.ShapeRange(i)
GoTo markShape
End If
Selection.MoveDown unit:=wdLine ' move down one line from anchor
End if
' ---
Set pRange = Selection.Range
pRange.End = ActiveDocument.Content.End
If pRange.ShapeRange.Count < 1 Then
MsgBox "No shapes after current selection"
Exit Sub
End If
For Each sHp In pRange.ShapeRange
Exit For
Next sHp
markShape:
' sHp is the desired shape
This works in most cases but it is possible to have more than one shape with the same name which could cause an endless loop if they have the same anchor. Also, the shapes are not necessarily in order in the document.

Frosty
09-12-2012, 02:01 PM
When you say shapes are not necessarily "in order" -- what do you mean? That the placement of the shape on the page may not be indicative of the order of the shapes in the .Shapes collection of whatever paragraph you're currently in?

I don't think that issue will be particularly solvable, unless you can give more criteria on what you would define as the "next" shape (lower? more to the right? both?).

It's hard to provide a compete solution when you only provide snippets of code. While providing snippets of code is nice, in that you are limiting how much you want us to read while trying to provide you a solution, it also limits the solutions.

Does the above code work? If so, great. If not, can you give better criteria by what you mean by "next" shape when there are multiple shapes anchored to a given paragraph?

Why do you need the functionality in code of going to the "next" shape-- what are you then doing? I assume a function which cycles through all shapes in the document, selecting each one in turn, is not desirable. So you're trying to do some kind of CTRL+DOWN functionality for shapes only (the way that works by jumping the cursor to the next paragraph) -- why?

Is using a mouse not good enough, when there are multiple shapes anchored to a single paragraph? Shapes are, by definition, a (typically) floating graphical object, not defined by linear anchor position in the document. In general, the standard way to interact with such shapes is to use the mouse.

What do you want to happen when there are groups of shapes? Select the group, then individual elements?

There is a potential can of worms thing going here... we might be able to help avoid that if you can give more of a "why" to this question.

smallken
09-12-2012, 05:18 PM
When editing a document, it is desirable to step through the shapes in a document and perhaps edit text if the shape is a text box or adjust its position on the page or chase up a link if it is linked to another source. As Word has 24 types of shapes, it is useful to display and alter a shape’s characteristics without needing to click through Word’s wretched menus.

I use a non-modal form containing buttons to advance to the next shape, display the shape’s parameters and carry out other functions such as changing the anchoring and the word wrap. The form is displayed all the time making it possible to select a shape and either manually change or use the change buttons on the form. Then, the next shape can be selected by clicking the Next Shape button. It is important to carry out the changes in order in the document because of the cascading changes to layout that can occur.

It is sometimes not immediately obvious if text on a page is in-line or is in a borderless textbox or table and the shapes search routine helps quickly identify these things. (I also have a find next table routine).

All of this is intended to make an editor’s life easier and more productive. When editing a 600 page document with 200 tables, 150 text boxes and numerous pictures and other shapes, any little help is worthwhile.

My post was intended to get suggestions for overcoming a knotty problem that I had. I only posted the relevant snippet of a much larger routine.

My web site showing the macros that I am developing is:
http://www.editordie.com.au/editorkae/index.html

Frosty
09-13-2012, 09:22 AM
Okay... but you didn't really answer the most pertinent question: how would you define the proper "order" of shapes anchored to the same paragraph?

I think some of your navigation tools will start to become obsolete with later versions of Word (the Selection pane shows all shapes, and their names, on the current page).

In addition, have you looked at the Application.Browser object? That is a quick way of navigating various elements of the document. It seems to me that you should be able to use that to navigate both graphics and tables, and then layer your own For each shape in selection.paragraph(1).shapes loop within that.

Application.Browser.Targer = wdBrowseGraphic
Application.Browser.Next (and .Previous, etc)

Not sure where to go from here... I totally understand consolidating disparate Word functionality for the purposes of a particular workflow. But I would suggest, where possible, using whatever native functionality exists, rather than trying to re-write your own version of that native functionality.

If there's anything else to do here, please let us know.

smallken
09-13-2012, 02:34 PM
Thanks for your comments.

Frosty
09-13-2012, 02:41 PM
Sure thing.

I noticed you said you have code which manipulates the anchor of a shape. Everytime I have tried to do that, is been kludgy. Are you doing something other than cutting and pasting the shape at a new range? Do you check whether the shapes positional info is relational or absolute?

Better ways of navigating elements of a document are very useful functions. Just wondering if you're reinventing the wheel on some of this, rather than simply displaying a more organized method for using built in functionality.

smallken
09-13-2012, 11:49 PM
I agree that I am to some extent re-inventing the wheel. I started in the days of Word97 and by the time Word2007 came along improvements to Word had made some of my stuff obsolete and I ditched much of it. The remainder is specific to editing and is very useful for those users.

These are some of Word’s foibles in relation to shapes that I have uncovered. I am sure that there are many more.


In Word 2010, shapes can be dragged up and down and the anchor point will move with the shape provided the anchor is not locked. It is also possible to drag onto a different page. Earlier Word versions had problems doing these things.

If you select the anchor paragraph, all shapes anchored to that paragraph are highlighted. If a shape has been dragged vertically then a “phantom” shape is also highlighted. The anchor point is at the start of the paragraph but you must select the whole paragraph in order to highlight the attached shapes.

There doesn’t seem to be a way of changing the anchor point in VBA. The anchor property is read only.

Shapes can be moved relative to the anchor by using the IncrementTop and IncrementLeft methods or by setting relative positions in the Layout menu.

There are two shape collections – Shapes and InlineShapes, as well as a Tables collection. If a shape object is converted between InlineShape and Shape it gets a split personality and is a member of both collections. This can introduce a problem with some of the methods that are present in one and not the other object.

Here is some code that shows a difference in result between Selection and Range objects. The First count is 0 and the Second count is 1 despite them referring to the same range.

Sub showAnchor()
' run with cursor in a shape or textbox
Dim aRange As Range
Selection.ShapeRange(1).Anchor.Paragraphs(1).Range.Select
MsgBox "First count=" & Selection.ShapeRange.Count
Set aRange = Selection.Range
MsgBox "Second count=" & aRange.ShapeRange.Count
End Sub

macropod
09-14-2012, 02:16 AM
To see what's involved in working with shapes via their location on the page, see:
http://www.msofficeforums.com/word-vba/13964-need-vba-macro-how-remove-specific-words.html#post39302
Although the code there is specifically for working with frames, working with other kinds of shapes is essentially the same.

macropod
09-14-2012, 02:20 AM
There doesn’t seem to be a way of changing the anchor point in VBA. The anchor property is read only.
You do that by working with the anchor's range - you can cut & paste it to wherever you want.

If a shape object is converted between InlineShape and Shape it gets a split personality and is a member of both collections.
Umm, no, they're strictly one or the other.

nihao15
09-18-2012, 01:18 AM
Sure thing. I noticed you said you have code which manipulates the anchor of a shape.

macropod
09-18-2012, 03:30 AM
Here's a fairly basic example. It relocates the first shape in the document to the first page. Test it with a shape on, say, the 2nd page.

Sub Test()
Dim Rng As Range
With ActiveDocument
Set Rng = .GoTo(What:=wdGoToPage, Name:="1")
.Shapes(1).Anchor.Cut
Rng.Paste
End With
End Sub
Note that, in real life, you'd probably want to get the shape's top & left settings so you can reposition it correctly at the new location.

smallken
09-24-2013, 03:03 AM
After two years, I finally got around to revisiting the problem and managed to devise a solution. The objective is to advance to and select the next (or previous) shape in the document whether the shape is floating or inline.

I followed up on Frosty's suggestion of using Application.Browser.Next. The result is the cursor at the anchor point of the next shape but the shape itself is not selected. In trying to determine which shape Browser has found, problems arise if more than one shapes is anchored to the same point.

Below is my solution. It is complex but it works. I am wondering if there is a simpler solution.


Sub getFollowingShape()
GetNextShape (True)
End Sub

Sub getPreviousShape()
GetNextShape (False)
End Sub

Function anchorsN() As Long ' number of shapes hanging off anchor
Dim anRange As Range
Set anRange = Selection.Range
anRange.MoveEnd unit:=wdCharacter
anchorsN = anRange.ShapeRange.Count
End Function

Sub GetNextShape(isNext As Boolean)
Dim aRange As Range
Dim shp As Shape
Dim j As Long
Dim mS As Long
Dim mI As Long
Dim kS As Long
Dim k As Long
Dim kA As Long
Dim kB As Long
Application.Browser.Target = wdBrowseGraphic
With Selection
mS = .Range.ShapeRange.Count
mI = .InlineShapes.Count
kS = .Range.Characters.Count ' =1 if only shape is selected
' ***************************
' if there are no shapes or there are more than one shape in selection
' then search from collapsed selection
If (mS = 0 And mI = 0) Or kS > 1 Or mS > 1 Or mI > 1 Then
kA = 1
kB = 1
If isNext Then
.Collapse direction:=wdCollapseStart
Else
.Collapse direction:=wdCollapseEnd
End If
Else ' selection is a single shape only with single character range
If mI < 1 Then
kA = anchorsN ' anchors from current shape
If isNext Then
Application.Browser.Next ' dance to pick up all anchors
Application.Browser.Previous
Else
Application.Browser.Previous ' fixes problem if first or last shape
Application.Browser.Next
End If
kB = anchorsN ' total number of shapes at anchor
kA = kB - kA + 1 ' position of current shape in anchor list
End If
End If
End With
' ***************************
If isNext Then
Application.Browser.Next
Else
Application.Browser.Previous
End If
' ***************************
' **** determine k = shape item no ******
If kB = 1 Then
If isNext Then
k = 1
Else
k = anchorsN
End If
Else ' multi shapes at anchor
If isNext Then
k = kA + 1
If k > kB Then k = 1
Else
k = kA - 1
If k < 1 Then k = 1
End If
End If
Set aRange = Selection.Range
With aRange
.MoveEnd unit:=wdCharacter
If .ShapeRange.Count > 0 Then
.ShapeRange(k).Select
Else
If .InlineShapes.Count > 0 Then .InlineShapes(1).Select
End If
End With
End Sub

SamT
09-24-2013, 05:10 PM
The remainder is specific to editing and is very useful for those users.

Shapes have been around since the dawn of Drawing Objects and have never really supposed to have been used as Controls, but it was the easiest quickest solution MS could come up with when Form type controls, (Forms Toolbar,) were needed.

How hard would it be to convert to all MsForms Controls, (Controls Toolbar?) They are a lot easier to program with IMO.

BTW, Thank you for returning with a solution to the problem. I'm going to mark it solved, but don't let that keep you or anybody else from continuing to improve your solution.

If you do decide to convert to MSForms controls, lets start another thread about them.