Selecting specific shapes to move to?

I'm trying to create a macro to move new objects over a set of pre-made template objects to intersect them, however I'm having some issues. I've included an example image below just to help understand what I'm talking about. Basically the red outline objects will be in the document from the start as a saved document. When I open the document I will be copy and pasting objects (represented by the blue objects) into the document. I will then move the objects on top of the red outlined ones (center aligned) and intersect them to create a new object.

My problem here is that I have no idea how to select which specific object I want moved to which template other than selecting only two objects at a time, which defeats the point of making a macro for time-saving. If I have every object in the document selected, how do I get the macro to move say... the triangle to the large square outline every time? 

The blue shapes are just examples, however I thought of potentially using colour as in indicator? The premade outline shapes will usually be colour coordinated by size. I have a few sets of templates so the large templates could be blue outlines, small templates are red etc. I would usually only be working with one set at a time, so all my templates in one session would be a single colour. 

I had a go at some coding earlier for this problem. This was mostly to see if there was some pattern in the order in which the shapes were selected in an SR to be moved. I selected 6 shapes to use in this example, so I expected two shapes in the corner of my selection range to be moved over each other since I put in the code to move the first object to the last object in the SR. This was not the case. I got consistent results, and the movement worked absolutely fine, but I just cant figure out why the shapes that were moved were the ones moved over the others. 

Private Sub Moveshape()
Dim s As Shape, sr As ShapeRange
Set sr = ActiveSelectionRange
ActiveDocument.ReferencePoint = cdrBottomLeft

Dim i As Integer
i = 1

Dim x As Double
Dim y As Double

For Each s In sr
If i = 1 Then
x = s.CenterX
y = s.CenterY
End If

If i = 6 Then
s.CenterX = x
s.CenterY = y
End If

i = i + 1
Next s


End Sub

This is causing me a real headache so any help you can provide would be greatly appreciated!!!

  • There are several ways you could do this but lets first discuss how a ShapeRange is ordered.

    If you marquee select a number of shapes and loop through the ShapeRange the first shape returned to you is going to be the lowest in the stacking order, the next shape will be one higher and so on.

    So lets look at a simple example. I have the following three shapes. A yellow, green and red square.

    If you look in the Object Manager the stacking order of these shapes from lowest to highest is:  green, yellow and red. It does not matter how they shapes are arranged on the page, its the stacking order that is important. So if you run the following code, we would expect that order to be returned to us, which it is.

    Sub ShowShapeOrder()
        Dim sr As ShapeRange
        Dim s As Shape
        Dim strOrder As String
        
        Set sr = ActiveSelectionRange
    
        For Each s In sr.Shapes
            If s Is sr.FirstShape Then
                strOrder = s.Fill.UniformColor.Name
            Else
                strOrder = strOrder & ", " & s.Fill.UniformColor.Name
            End If
        Next s
        
        MsgBox strOrder
    End Sub
    

    The code above will return:  Green, Yellow, Red

    If you wanted the shapes to be returned in the opposite order you could reverse the ShapeRange like this:

    Sub ShowShapeOrderReversed()
        Dim sr As ShapeRange
        Dim s As Shape
        Dim strOrder As String
        
        Set sr = ActiveSelectionRange.ReverseRange
    
        For Each s In sr.Shapes
            If s Is sr.FirstShape Then
                strOrder = s.Fill.UniformColor.Name
            Else
                strOrder = strOrder & ", " & s.Fill.UniformColor.Name
            End If
        Next s
        
        MsgBox strOrder
    End Sub
    

    The code above will return Red, Yellow, Green

    Shift selecting shapes works in a similar manor. If I shift select the shapes left to right, yellow, green, red the first code sample will return these in the opposite order: Red, Green, Yellow. If I use the second code example they will be return in the same order that I selected them. Yellow, Green, Red.

    Now one more, if you use the ActivePage.Shapes.All the shapes are going to be returned from highest stacking order to the least. We will use this in my later example.

    So there you have it, ShapeRange order in a nutshell. Now that we understand how the order works, lets apply it to your problem. Here is how I have my document set up:

    As you can see, I have three shapes that have a red outline and three that are filled with blue. I marquee select with three will the blue. The following code will create a ShapeRange of all the shapes on the page. It then creates another ShapeRange of my selection. I then remove my selection from the ShapeRange which had all the shapes. This leaves me with a ShapeRange that has only the shapes with the red outlines and one with the shapes with the blue fill. 

    I then loop through my selection ShapeRange with the shapes with the blue shapes. I will move each of these to the shape that has the same order index.

    Sub MoveShapes()
        Dim sr As ShapeRange, srSelection As ShapeRange
        Dim s As Shape
        Dim lngIndex As Long
        
        ActiveDocument.ReferencePoint = cdrCenter
        
        Set sr = ActivePage.Shapes.All
        Set srSelection = ActiveSelectionRange.ReverseRange
        sr.RemoveRange srSelection
        
        For Each s In srSelection.Shapes
            lngIndex = srSelection.IndexOf(s)
            s.SetPosition sr(lngIndex).PositionX, sr(lngIndex).PositionY
        Next s
    End Sub
    

    All the shapes should then match up. 

    Trying to match based on the Stacking Order is not very intuitive. Another way you could do this it to actually name your shapes. This would make it much easier. You would not need to worry about the order, each shape would just match itself to the other shape with the same name. Here is how it looks when I have it set up. You will see I have the shapes named: Rect 1, Triangle 1 and Ellipse 1.

    The code:

    Sub MoveShapesByName()
        Dim sr As ShapeRange, srSelection As ShapeRange
        Dim s As Shape, sNamed As Shape
        Dim lngIndex As Long
        
        ActiveDocument.ReferencePoint = cdrCenter
        
        Set sr = ActivePage.Shapes.All
        Set srSelection = ActiveSelectionRange.ReverseRange
        sr.RemoveRange srSelection
        
        For Each s In srSelection.Shapes
            Set sName = sr.Shapes.FindShape(s.Name)
            s.SetPosition sName.PositionX, sName.PositionY
        Next s
    End Sub
    

    There are of course several other ways you could also do this, but hopefully this gets you started. 

    Happy Coding, 

    -Shelby