In a macro I'm working on, I allow the user to "store" selected items in a Shaperange, and also to select and add additional items to that Shaperange.
This is done so that the items in the ShapeRange can then be used to create an active selection at a later time.
At some points in time, I want to check to see if all of the items in that Shaperange still exist in the document. Specifically, if all of the items that were stored in the Shaperange were later deleted, I want to be able to disable the "Restore saved selection" button on the form.
Is there a way to check whether one of those referenced items still exists? So that I can "clean up" the Shaperange so that it only contains items that exist?
I could record the active selection, use the Shaperange to create a selection, set the Shaperange to that selection, then restore that saved selection - but that would create problems with my "responsive" macro.
You could make a quick for each shape in shaperange loop function and for each shape use a function like the one below..
if shapexists (shape) = false then shaperange.remove shape
Public Function ShapeExists(ByRef S1 As Shape) As Boolean On Error GoTo NotFound If S1.Name = S1.Name Then ShapeExists = True Exit Function NotFound: ShapeExists = False End Function
Howardhopkins said:You could make a quick for each shape in shaperange loop function and for each shape use a function like the one below..if shapexists (shape) = false then shaperange.remove shape
I did it using a Do loop and walking through the shaperange by index. When ShapeExists returns False, I remove the nonexistent shape by index.
Public Sub SR_remove_nonexisting(ByRef Shaperange As Shaperange)Dim lngSR_index As Long If Shaperange.Count > 0 Then lngSR_index = 1 Do If ShapeExists(Shaperange(lngSR_index)) = False Then Shaperange.Remove lngSR_index Else lngSR_index = lngSR_index + 1 End If Loop Until lngSR_index > Shaperange.Count End IfEnd Sub
I don't know that I could do that using a "for each shape in shaperange" approach. That would sometimes be trying to refer to a nonexistent shape, wouldn't it?
Glad I could help.
You make a good point with my original suggestion of a for each loop I didn't think that first part through at all! Even if instantiating a none existent shape from the range didn't cause an issue you would probably create confusion by removing it from the range while actively using it to index your shapes.
Your solution is the best way I can think of!
OK, I hadn't tested it quite enough; now added a simple check so that the sub works properly if shaperange.count = 0.
another thing I find useful (if a little defensive) is to test for a shaperange to actually exist
If not shaperange is nothing or shaperange.count > 0 then ... run code
you have to use the double negative to test for a strangeranges existence so it might require a little rejigging of your code but well worth it!
when you release macros to other users or want to reuse methods as much as possible it's a good idea to check both!
That's a very good suggestion!
In this case, I was checking the existence of the shaperange before calling this sub, but I see the wisdom of "build it right, build it once" to have something that will work reliably in more circumstances.
I broke out the two checks into two separate lines instead of using an "or", as I think that would still fail on the shaperange.count if the shaperange didn't exist.
Public Sub SR_remove_nonexisting(ByRef Shaperange As Shaperange)Dim lngSR_index As Long If Not Shaperange Is Nothing Then If Shaperange.Count > 0 Then lngSR_index = 1 Do If ShapeExists(Shaperange(lngSR_index)) = False Then Shaperange.Remove lngSR_index Else lngSR_index = lngSR_index + 1 End If Loop Until lngSR_index > Shaperange.Count End If End IfEnd Sub
I can't test this now as I am in the middle of decorating my house!
Though I've used that line a lot in my own codebase a lot so I assume 'if or' statements check in the sequence they're written in. So as long as you test for the shape for 'not' being nothing before using the 'or' for checking any properties of the range/shape it'll work fine.
It is essentially the same thing as you have above just comes downs to personal preference.
Does anyone have any further insight into how if/or statements work? and is it the same with compiled code like C#?
I tried the "or" version, and it failed if the shaperange didn't exist. When you have the opportunity, play with it a bit to see.
Trying to learn more (what I don't know about this would fill a book) I have read that VBA does not have "short-circuit evaluation".
Even when outright failure is not a consideration, some take that into account in order to avoid calling an "expensive" function when it isn't strictly necessary; e.g., can't use the first part of an "and" to avoid calling an expensive function in the second part.
I've just tested it and you sir are correct again! and that is a very helpful thing to know. Every day a school day with programming!
What programming I have been doing lately is with learning C# the proper way (using proper training resources as opposed to tinkering in the VBA) and it always amazes me how many assumptions I have about programming are incorrect.
Looking through my code I had actually used it like my suggestion but prior to using the function, I've used the old 'set SR = NEW SHAPERANGE' so testing it for nothing was completely redundant anyhow! I'll be fixing that right now! Thanks for the insight!