简体   繁体   中英

PowerPoint Interop: Get Placeholder by Name defined in Master

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的主人

Presentation where names defined in master are lost: 演示文稿的屏幕截图

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

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?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM