Detecting the deletion or grouping of a shape

M

Marty

Howdy All,

There are many spots where my routines draw a graphic (such as an oval or a rectangle) and then sets up an object to track that graphic. For example, at some later point the code might need to change the forecolor of all shapes held in AShapesSubset collection. A problem arises if the user should delete or group one of the shapes represented in the AShapesSubset collection. The collection's pointer for the affected shape is no longer valid, and any attempt to change the forecolor produces an error.

Is there a "best practice" means of tracking shapes? I wish that shapes had events associated with them; e.g. "ShapeDeleted" and a "ShapeGrouped" events. It appears that this is not the case (at least in Word 2000). Alternatively, is there a way to alter Word's Delete and Group routines so that I can first inspect the ShapeRange affected and post my own event? I can, of course, write code that would search through all Shapes and all GroupShapes to check on a shape's status. But that seems like quite a lot of overhead to encompass every single time I need to refer to a shape.

Marty
 
C

Chad DeMeyer

Use the following If Then statement to determine whether or not the shape is
present before attempting to change the forecolor or otherwise access the
member of the collection. Assuming the shape was referred to in code as
AShapesSubset(i):

If Not (AShapesSubset(i) Is Nothing) Then
... 'actions to perform on the shape
End If

Regards,
Chad DeMeyer


Marty said:
Howdy All,

There are many spots where my routines draw a graphic (such as an oval
or a rectangle) and then sets up an object to track that graphic. For
example, at some later point the code might need to change the forecolor of
all shapes held in AShapesSubset collection. A problem arises if the user
should delete or group one of the shapes represented in the AShapesSubset
collection. The collection's pointer for the affected shape is no longer
valid, and any attempt to change the forecolor produces an error.
Is there a "best practice" means of tracking shapes? I wish that
shapes had events associated with them; e.g. "ShapeDeleted" and a
"ShapeGrouped" events. It appears that this is not the case (at least in
Word 2000). Alternatively, is there a way to alter Word's Delete and Group
routines so that I can first inspect the ShapeRange affected and post my own
event? I can, of course, write code that would search through all Shapes
and all GroupShapes to check on a shape's status. But that seems like quite
a lot of overhead to encompass every single time I need to refer to a shape.
 
J

Jezebel

The quick work-around is simply to trap the error:

on error resume next
shp.Fill.ForeColor.RGB = vbBlue
on error goto 0

if Err.Number <> 0 then
set shp = nothing 'object no longer exists
end if





Marty said:
Howdy Chad,

I had a problem making your suggestion work, perhaps I missunderstood
your suggestion. Suppose that I set up a shape object as follows:
Dim shp As Shape
Set shp = ActiveDocument.Shapes(1)

I let this code run (using the debugger), and I stop it immediately after
the Set statement. I then switch to the Word document and click on the
shape to which "shp" is pointing, then delete that shape. You might think
that deleting a-shape-in-the-document would also set all
VBA-pointers-to-the-shape to Nothing, right? But then if I switch back to
basic and let the debugger run the following code:
If shp Is Nothing Then
MsgBox "Pointer to deleted shape is nothing"
Else
shp.Fill.ForeColor.RGB = vbBlue
End If

then the test of "shp is Nothing" results in "False", control passes to
the forecolor assignment, and that statement blows up. The error number is
5825 and the description is "object has been deleted".
In summary, we can manually delete the shape-in-the-document without
setting pointers to that shape to Nothing.
For a while I was using the 5825 error code as a means of detecting if
shapes were deleted. However, that approach is not very comfortable; it is
not a documented method and Microsoft could change the error codes at any
time. Besides, it makes debugging more cumbersome (due to extra breaks).
I'm hoping that someone might have a better approach.
 
J

Jean-Guy Marcil

Bonjour,

Dans son message, < Marty > écrivait :
In this message, < Marty > wrote:

|| Howdy All,
||
|| There are many spots where my routines draw a graphic (such as an
oval or a rectangle) and
|| then sets up an object to track that graphic. For example, at some later
point the code might
|| need to change the forecolor of all shapes held in AShapesSubset
collection. A problem arises
|| if the user should delete or group one of the shapes represented in the
AShapesSubset
|| collection. The collection's pointer for the affected shape is no longer
valid, and any attempt
|| to change the forecolor produces an error.
||
|| Is there a "best practice" means of tracking shapes? I wish that
shapes had events
|| associated with them; e.g. "ShapeDeleted" and a "ShapeGrouped" events.
It appears that this is
|| not the case (at least in Word 2000). Alternatively, is there a way to
alter Word's Delete and
|| Group routines so that I can first inspect the ShapeRange affected and
post my own event? I
|| can, of course, write code that would search through all Shapes and all
GroupShapes to check on
|| a shape's status. But that seems like quite a lot of overhead to
encompass every single time I
|| need to refer to a shape.
||

I would use the Name property to assign my own names to the shapes.

Later, I would use an InStr function to check if the shapes with my names
are still present.
Finally, check for grouped shapes, you can also "interrogate" members of a
group without ungrouping it.

Paste that code in a blank document module and play around with it to see
what you can do. Maybe you will find some useful stuff in there!

'_______________________________________
Const ShapeName As String = "MyShapes"

'_______________________________________
Sub AddShapes()

Dim MyShape As Shape

Set MyShape = ActiveDocument.Shapes _
.AddShape(msoShapeRectangle, _
113.8, 91.75, 149.8, 84.2)
MyShape.Name = ShapeName & "Rect1"

Set MyShape = ActiveDocument.Shapes _
.AddShape(msoShapeOval, _
340.25, 158.5, 102.8, 110.35)
MyShape.Name = ShapeName & "Ov1"

Set MyShape = ActiveDocument.Shapes _
.AddShape(msoShapeRectangle, _
127.15, 235.15, 36.6, 46.45)
MyShape.Name = ShapeName & "Rect2"

Set MyShape = Nothing

End Sub
'_______________________________________

'_______________________________________
Sub DoStuffWithShapes()

Dim MyShape As Shape
Dim SubMyShape As Shape
Dim i As Long
Const RCol As Long = 255
Const GCol As Long = 0
Const BCol As Long = 0
Dim FoundOne As Boolean

FoundOne = False

For Each MyShape In ActiveDocument.Shapes
With MyShape
If .Type = msoGroup Then
For i = 1 To .GroupItems.Count
Set SubMyShape = .GroupItems(i)
With SubMyShape
If InStr(.Name, ShapeName) > 0 Then
.Fill.ForeColor.RGB = RGB(RCol, GCol, BCol)
FoundOne = True
End If
End With
Next i
End If
If InStr(.Name, ShapeName) > 0 Then
.Fill.ForeColor.RGB = RGB(RCol, GCol, BCol)
FoundOne = True
End If
End With
Next MyShape

If Not FoundOne Then
MsgBox "No valid shapes were found", vbInformation, "No Shapes"
End If

Set MyShape = Nothing
Set SubMyShape = Nothing

End Sub
'_______________________________________

--
Salut!
_______________________________________
Jean-Guy Marcil - Word MVP
(e-mail address removed)
Word MVP site: http://www.word.mvps.org
 
M

Marty

Bonjour Jean-Guy Marcil,

Thanks for your reply. I am leaning in that direction. I think that I will set up a ShapeHandler class that will accept any Office shape as a property. It will then provide a GetStatus method that will use the logic that you outlined to tell the client if the passed shape was:
- a basic shape (eg a rectangle or autoshapein ActiveDocument.Shapes
- a GroupShapes collection in Shapes,
- an item in a GroupShapes collection
- deleted.

Additionally, it will provide a "Delete" method that will do the proper job of deleting the shape, regardless of its status. I may also include a GetGroupShapes method. That would simplify the handling of a shape that is an item in a GroupShape.

Marty
 
J

Jean-Guy Marcil

Bonjour,

Dans son message, < Marty > écrivait :
In this message, < Marty > wrote:

|| Bonjour Jean-Guy Marcil,
||
|| Thanks for your reply. I am leaning in that direction. I think that
I will set up a
|| ShapeHandler class that will accept any Office shape as a property. It
will then provide a
|| GetStatus method that will use the logic that you outlined to tell the
client if the passed
|| shape was:
|| - a basic shape (eg a rectangle or autoshapein ActiveDocument.Shapes
|| - a GroupShapes collection in Shapes,
|| - an item in a GroupShapes collection
|| - deleted.
||
|| Additionally, it will provide a "Delete" method that will do the
proper job of deleting the
|| shape, regardless of its status. I may also include a GetGroupShapes
method. That would
|| simplify the handling of a shape that is an item in a GroupShape.
||

Looks like you've got it all sorted out!
Would love to see any code you might come up with. I am trying to learn more
about classes... this would be a great learning experience for me!

TIA

BTW:
|| GetStatus method that will use the logic that you outlined to tell the
client if the passed
|| shape was:
<snip>
|| - deleted.
How can you tell if a passed shape was deleted? I mean, if it was deleted,
it cannot be passed.... So how do you get the status of something that
cannot be passed as it is not there anymore?!?

--
Salut!
_______________________________________
Jean-Guy Marcil - Word MVP
(e-mail address removed)
Word MVP site: http://www.word.mvps.org
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top