3

In my PowerPoint AddIn I want to access shapes on the slides. The shapes are placeholders defined in custom layouts in the slidemaster.

When I add a slide based on the custom layout, the shapes just get named "placeholder 1", "placeholder 2", ...

Is there a way to get the placeholder by the name given in the master?

Currently I am searching shapes with this code:

public static Shape GetShape(string stringToSearch, Shapes shapes) {

        foreach (Shape shape in shapes) {

            if (shape.Name == stringToSearch) {
                return shape;
            }

            // Search Groups
            if (shape.Type == MsoShapeType.msoGroup) {
                foreach (Shape childshape in shape.GroupItems) {
                    if (childshape.Name == stringToSearch) {
                        return childshape;
                    }
                }
            }
        }

        throw new KeyNotFoundException("No Shape found");
}

Update: Maybe to make it more clear, this is the structure of the PowerPoint-Presentation.

Master with Names defined for placeholders: Scrennshot of the master

Presentation where names defined in master are lost: Screenshot of the presentation

Problem: How to get element in presentation, by name defined in master?

Tobias
  • 2,945
  • 5
  • 41
  • 59
  • What type of placeholders are on the custom layout and how have they been added/named? It looks to me as though the shapes on slides match the names of shapes on the custom layouts UNLESS you rename the shape on the custom layout; then the matching shape on the slide gets a name like [PlaceholderType] ## (and the number seems to have no relationship to the name of the shape on the master/layout). In that case, you might need to iterate the shapes collection on slide and layout looking for a matching placeholder type. – Steve Rindsberg Nov 15 '13 at 14:23
  • Then if there's more than one of a given placeholder type you might need to duplicate the slide temporarily and reapply the layout (so the shapes on the slide resume their original position) and then find the correct master/layout shape by comparing size/position. Ugly! – Steve Rindsberg Nov 15 '13 at 14:25
  • One is an imagePlace-Holder and the other one a text placeholder. Placeholders are named picImage and txtName. – Tobias Nov 15 '13 at 14:32
  • Which means that somebody has renamed them from whatever PPT assigned as the default name when they were created, so you'll have to work extra hard to sort it out. Sorry! – Steve Rindsberg Nov 15 '13 at 23:32
  • If "somebody" means PowerPoint then you are right. Nobody renamed something. It is default behaviour of PowerPoint to name placeholders "Placeholder 1", "Placeholder 2", ... – Tobias Nov 18 '13 at 07:58
  • That's not what it does here (version 2010). Start with the default blank template and see what happens there. – Steve Rindsberg Nov 19 '13 at 02:24

1 Answers1

0

Well.. here is Steve's "Ugly!" solution.

For my project I neither have nor want control over creation of shapes so I cannot "tag" them. That is why I have a custom placeholder name to identify them, So the names of shapes will indeed be [PlaceholderType] ##.

The steps:

  • store the locations of all shapes
  • reset the slide layout
  • match slide shapes and master slide shapes
  • restore the locations of all shapes.

Note: I don't use shape groups. this technique will get a lot more complicated if you do and you need to check inside groups.

This is the function that does that and returns a mastershapename - shapename mapping.

private Dictionary<string, string> GetShapeMasters(Powerpoint.Slide s)
{
    Dictionary<string, string> shapeMasters = new Dictionary<string, string>();
    List<ShapeLocation> shapeLocations = new List<ShapeLocation>();

    //store locations
    foreach (Powerpoint.Shape sh in s.Shapes)
    {
        shapeLocations.Add(new ShapeLocation()
        {
            Name = sh.Name,
            Location = new System.Drawing.RectangleF(sh.Left, sh.Top, sh.Width, sh.Height)
        });
    }

    //have powerpoint reset the slide
    //ISSUE: this changes the names of placeholders without content.
    s.CustomLayout = s.CustomLayout;

    //compare slide and master
    foreach (Powerpoint.Shape sh in s.Shapes)
    {
        foreach (Powerpoint.Shape msh in s.CustomLayout.Shapes)
        {
            if (IsShapeMaster(sh, msh))
            {
                shapeMasters[msh.Name] = sh.Name;
            }
        }
    }

    //restore locations
    //TODO: might be replaced by undo
    foreach (var shm in shapeLocations)
    {
        Powerpoint.Shape sh = null;
        try
        {
            sh = s.Shapes[shm.Name];
        }
        catch 
        {
            //Fails for renamed placeholder shapes.
            //Have yet to find a decent way to check if a shape name exists.
        }

        //placeholders do not need to be restored anyway.
        if (sh != null)
        {
            sh.Left = shm.Location.Left;
            sh.Top = shm.Location.Top;
            sh.Width = shm.Location.Width;
            sh.Height = shm.Location.Height;
        }
    }

    return shapeMasters;
}

With this you can do

Dictionary<string, string> shapeMasters = GetShapeMasters(theSlide);
if(shapeMasters.ContainsKey(stringToSearch))
    Powerpoint.Shape KnownShape = theSlide[shapeMasters[stringToSearch]];

And here is the comparison function that takes two shapes and checks if they are "equal". Could be extended to make it more precise.

private bool IsShapeMaster(Powerpoint.Shape sh, Powerpoint.Shape msh)
{
    return
        sh.Left == msh.Left
        && sh.Top == msh.Top
        && sh.Width == msh.Width
        && sh.Height == msh.Height
        && sh.Type == msh.Type
        && sh.PlaceholderFormat.Type == msh.PlaceholderFormat.Type;
}

Little class that stores original shape location

class ShapeLocation
{
    public string Name;
    public System.Drawing.RectangleF Location;
}

I'm open to suggestions because I don't like this, either. Only there seems to be no other way to link shapes and placeholders together. There really isn't some shape.MasterShape we are missing, is there?

H B
  • 701
  • 6
  • 9