将新幻灯片插入到演示文稿

本主题演示如何使用 Open XML SDK 中的类以编程方式将新幻灯片插入演示文稿。

获取 PresentationDocument 对象

在 Open XML SDK 中 PresentationDocument , 类表示演示文稿文档包。 若要处理演示文稿文档,请先创建 类的 PresentationDocument 实例,然后使用该实例。 若要从文档创建类实例, Open 请调用使用文件路径的方法,并使用布尔值作为第二个参数来指定文档是否可编辑。 若要打开文档进行读/写,请指定此参数的值 true ,如以下 using 语句所示。 在此代码段中, presentationFile 参数是一个字符串,表示要从中打开文档的文件的完整路径。

// Open the source document as read/write. 
using (PresentationDocument presentationDocument = PresentationDocument.Open(presentationFile, true))

在 v3.0.0+ 中, Close() 已删除 方法,转而依赖于 using 语句。 这可确保在 Dispose() 到达右大括号时自动调用 方法。 语句后面的 using 块为在 语句中创建 using 或命名的对象建立作用域,在本例 presentationDocument中为 。

基本演示文稿文档结构

文档的基本文档结构PresentationML由多个部分组成,其中包括包含表示定义的main部分。 ISO/IEC 29500 规范中的以下文本介绍了包的整体PresentationML形式。

包的main部分PresentationML以演示文稿根元素开头。 该元素包含演示文稿,演示文稿又引用幻灯片 列表、幻灯片母版 列表、备注母版 列表和讲义母版 列表。 幻灯片列表指的是演示文稿中的所有幻灯片;幻灯片母版列表指的是演示文稿中使用的全部幻灯片母版;备注母版包含有关备注页格式的信息;讲义母版描述讲义的外观。

讲义 是可提供给访问群体 的一组打印的幻灯片。

除了文本和图形,每个幻灯片还可以包含注释备注,可以具有布局,并且可以是一个或多个自定义演示文稿 的组成部分。 注释是供维护演示文稿幻灯片平台的人员参考的批注。 备注是供演示者或访问群体参考的提醒信息或一段文字。

文档可以包括以下其他功能 PresentationML动画音频视频和幻灯片之间的 切换 效果。

文档 PresentationML 不作为一个大正文存储在单个部件中。 而实现某些功能组合的元素会存储在各个部件中。 例如,文档中的所有作者都存储在一个作者部件中,而每个幻灯片都有自己的部分。

ISO/IEC 29500:2016

以下 XML 代码示例代表包含用 ID 267 和 256 表示的两个幻灯片的演示文稿。

    <p:presentation xmlns:p="…" … > 
       <p:sldMasterIdLst>
          <p:sldMasterId
             xmlns:rel="https://…/relationships" rel:id="rId1"/>
       </p:sldMasterIdLst>
       <p:notesMasterIdLst>
          <p:notesMasterId
             xmlns:rel="https://…/relationships" rel:id="rId4"/>
       </p:notesMasterIdLst>
       <p:handoutMasterIdLst>
          <p:handoutMasterId
             xmlns:rel="https://…/relationships" rel:id="rId5"/>
       </p:handoutMasterIdLst>
       <p:sldIdLst>
          <p:sldId id="267"
             xmlns:rel="https://…/relationships" rel:id="rId2"/>
          <p:sldId id="256"
             xmlns:rel="https://…/relationships" rel:id="rId3"/>
       </p:sldIdLst>
           <p:sldSz cx="9144000" cy="6858000"/>
       <p:notesSz cx="6858000" cy="9144000"/>
    </p:presentation>

使用 Open XML SDK,可以使用对应于 PresentationML 元素的强类型类创建文档结构和内容。 可以在 命名空间中找到 这些类。 下表列出了对应于 、、 sldLayoutsldMasternotesMaster 元素的类的sld类名。

PresentationML 元素 Open XML SDK 类 说明
<sld/> Slide 演示文稿幻灯片。 它是 SlidePart 的根元素。
<sldLayout/> SlideLayout 幻灯片版式。 它是 SlideLayoutPart 的根元素。
<sldMaster/> SlideMaster 幻灯片母版。 它是 SlideMasterPart 的根元素。
<notesMaster/> NotesMaster 备注母版(或讲义母版)。 它是 NotesMasterPart 的根元素。

示例代码的工作方式

示例代码由 方法的两个 InsertNewSlide 重载组成。 第一个重载方法使用三个参数:一个表示要向其中添加幻灯片的演示文稿文件的完整路径、一个表示演示文稿中要添加幻灯片的从零开始的幻灯片索引位置的整数,和一个表示新幻灯片的标题的字符串。 它以读/写的形式打开演示文稿文件,获取对象 PresentationDocument ,然后将该对象传递给执行插入的第二个重载 InsertNewSlide 方法。

// Insert a slide into the specified presentation.
public static void InsertNewSlide(string presentationFile, int position, string slideTitle)
{
    // Open the source document as read/write. 
    using (PresentationDocument presentationDocument = PresentationDocument.Open(presentationFile, true))
    {
        // Pass the source document and the position and title of the slide to be inserted to the next method.
        InsertNewSlide(presentationDocument, position, slideTitle);
    }
}

第二个重载 InsertNewSlide 方法创建一个新 Slide 对象,设置其属性,然后将其插入演示文稿中的幻灯片顺序中。 此方法的第一部分会创建幻灯片并设置其属性。

// Insert the specified slide into the presentation at the specified position.
public static SlidePart InsertNewSlide(PresentationDocument presentationDocument, int position, string slideTitle)
{
    PresentationPart? presentationPart = presentationDocument.PresentationPart;

    // Verify that the presentation is not empty.
    if (presentationPart is null)
    {
        throw new InvalidOperationException("The presentation document is empty.");
    }

    // Declare and instantiate a new slide.
    Slide slide = new Slide(new CommonSlideData(new ShapeTree()));
    uint drawingObjectId = 1;

    // Construct the slide content.            
    // Specify the non-visual properties of the new slide.
    CommonSlideData commonSlideData = slide.CommonSlideData ?? slide.AppendChild(new CommonSlideData());
    ShapeTree shapeTree = commonSlideData.ShapeTree ?? commonSlideData.AppendChild(new ShapeTree());
    NonVisualGroupShapeProperties nonVisualProperties = shapeTree.AppendChild(new NonVisualGroupShapeProperties());
    nonVisualProperties.NonVisualDrawingProperties = new NonVisualDrawingProperties() { Id = 1, Name = "" };
    nonVisualProperties.NonVisualGroupShapeDrawingProperties = new NonVisualGroupShapeDrawingProperties();
    nonVisualProperties.ApplicationNonVisualDrawingProperties = new ApplicationNonVisualDrawingProperties();

    // Specify the group shape properties of the new slide.
    shapeTree.AppendChild(new GroupShapeProperties());

第二个重载 InsertNewSlide 方法的下一部分将标题形状添加到幻灯片并设置其属性,包括其文本。

// Declare and instantiate the title shape of the new slide.
Shape titleShape = shapeTree.AppendChild(new Shape());

drawingObjectId++;

// Specify the required shape properties for the title shape. 
titleShape.NonVisualShapeProperties = new NonVisualShapeProperties
    (new NonVisualDrawingProperties() { Id = drawingObjectId, Name = "Title" },
    new NonVisualShapeDrawingProperties(new Drawing.ShapeLocks() { NoGrouping = true }),
    new ApplicationNonVisualDrawingProperties(new PlaceholderShape() { Type = PlaceholderValues.Title }));
titleShape.ShapeProperties = new ShapeProperties();

// Specify the text of the title shape.
titleShape.TextBody = new TextBody(new Drawing.BodyProperties(),
        new Drawing.ListStyle(),
        new Drawing.Paragraph(new Drawing.Run(new Drawing.Text() { Text = slideTitle })));

第二个重载 InsertNewSlide 方法的下一部分将正文形状添加到幻灯片并设置其属性,包括其文本。

// Declare and instantiate the body shape of the new slide.
Shape bodyShape = shapeTree.AppendChild(new Shape());
drawingObjectId++;

// Specify the required shape properties for the body shape.
bodyShape.NonVisualShapeProperties = new NonVisualShapeProperties(new NonVisualDrawingProperties() { Id = drawingObjectId, Name = "Content Placeholder" },
        new NonVisualShapeDrawingProperties(new Drawing.ShapeLocks() { NoGrouping = true }),
        new ApplicationNonVisualDrawingProperties(new PlaceholderShape() { Index = 1 }));
bodyShape.ShapeProperties = new ShapeProperties();

// Specify the text of the body shape.
bodyShape.TextBody = new TextBody(new Drawing.BodyProperties(),
        new Drawing.ListStyle(),
        new Drawing.Paragraph());

第二个重载 InsertNewSlide 方法的最后一部分将创建新的幻灯片部件,查找插入幻灯片的指定索引位置,然后将其插入,并将新幻灯片分配给新的幻灯片部件。

// Create the slide part for the new slide.
SlidePart slidePart = presentationPart.AddNewPart<SlidePart>();

// Assign the new slide to the new slide part
slidePart.Slide = slide;

// Modify the slide ID list in the presentation part.
// The slide ID list should not be null.
SlideIdList? slideIdList = presentationPart.Presentation.SlideIdList;

// Find the highest slide ID in the current list.
uint maxSlideId = 1;
SlideId? prevSlideId = null;

OpenXmlElementList slideIds = slideIdList?.ChildElements ?? default;

foreach (SlideId slideId in slideIds)
{
    if (slideId.Id is not null && slideId.Id > maxSlideId)
    {
        maxSlideId = slideId.Id;
    }

    position--;
    if (position == 0)
    {
        prevSlideId = slideId;
    }

}

maxSlideId++;

// Get the ID of the previous slide.
SlidePart lastSlidePart;

if (prevSlideId is not null && prevSlideId.RelationshipId is not null)
{
    lastSlidePart = (SlidePart)presentationPart.GetPartById(prevSlideId.RelationshipId!);
}
else
{
    string? firstRelId = ((SlideId)slideIds[0]).RelationshipId;
    // If the first slide does not contain a relationship ID, throw an exception.
    if (firstRelId is null)
    {
        throw new ArgumentNullException(nameof(firstRelId));
    }

    lastSlidePart = (SlidePart)presentationPart.GetPartById(firstRelId);
}

// Use the same slide layout as that of the previous slide.
if (lastSlidePart.SlideLayoutPart is not null)
{
    slidePart.AddPart(lastSlidePart.SlideLayoutPart);
}

// Insert the new slide into the slide list after the previous slide.
SlideId newSlideId = slideIdList!.InsertAfter(new SlideId(), prevSlideId);
newSlideId.Id = maxSlideId;
newSlideId.RelationshipId = presentationPart.GetIdOfPart(slidePart);

示例代码

以下是使用 C# 和 Visual Basic 编写的完整示例代码。

// Insert a slide into the specified presentation.
public static void InsertNewSlide(string presentationFile, int position, string slideTitle)
{
    // Open the source document as read/write. 
    using (PresentationDocument presentationDocument = PresentationDocument.Open(presentationFile, true))
    {
        // Pass the source document and the position and title of the slide to be inserted to the next method.
        InsertNewSlide(presentationDocument, position, slideTitle);
    }
}
// Insert the specified slide into the presentation at the specified position.
public static SlidePart InsertNewSlide(PresentationDocument presentationDocument, int position, string slideTitle)
{
    PresentationPart? presentationPart = presentationDocument.PresentationPart;

    // Verify that the presentation is not empty.
    if (presentationPart is null)
    {
        throw new InvalidOperationException("The presentation document is empty.");
    }

    // Declare and instantiate a new slide.
    Slide slide = new Slide(new CommonSlideData(new ShapeTree()));
    uint drawingObjectId = 1;

    // Construct the slide content.            
    // Specify the non-visual properties of the new slide.
    CommonSlideData commonSlideData = slide.CommonSlideData ?? slide.AppendChild(new CommonSlideData());
    ShapeTree shapeTree = commonSlideData.ShapeTree ?? commonSlideData.AppendChild(new ShapeTree());
    NonVisualGroupShapeProperties nonVisualProperties = shapeTree.AppendChild(new NonVisualGroupShapeProperties());
    nonVisualProperties.NonVisualDrawingProperties = new NonVisualDrawingProperties() { Id = 1, Name = "" };
    nonVisualProperties.NonVisualGroupShapeDrawingProperties = new NonVisualGroupShapeDrawingProperties();
    nonVisualProperties.ApplicationNonVisualDrawingProperties = new ApplicationNonVisualDrawingProperties();

    // Specify the group shape properties of the new slide.
    shapeTree.AppendChild(new GroupShapeProperties());
    // Declare and instantiate the title shape of the new slide.
    Shape titleShape = shapeTree.AppendChild(new Shape());

    drawingObjectId++;

    // Specify the required shape properties for the title shape. 
    titleShape.NonVisualShapeProperties = new NonVisualShapeProperties
        (new NonVisualDrawingProperties() { Id = drawingObjectId, Name = "Title" },
        new NonVisualShapeDrawingProperties(new Drawing.ShapeLocks() { NoGrouping = true }),
        new ApplicationNonVisualDrawingProperties(new PlaceholderShape() { Type = PlaceholderValues.Title }));
    titleShape.ShapeProperties = new ShapeProperties();

    // Specify the text of the title shape.
    titleShape.TextBody = new TextBody(new Drawing.BodyProperties(),
            new Drawing.ListStyle(),
            new Drawing.Paragraph(new Drawing.Run(new Drawing.Text() { Text = slideTitle })));
    // Declare and instantiate the body shape of the new slide.
    Shape bodyShape = shapeTree.AppendChild(new Shape());
    drawingObjectId++;

    // Specify the required shape properties for the body shape.
    bodyShape.NonVisualShapeProperties = new NonVisualShapeProperties(new NonVisualDrawingProperties() { Id = drawingObjectId, Name = "Content Placeholder" },
            new NonVisualShapeDrawingProperties(new Drawing.ShapeLocks() { NoGrouping = true }),
            new ApplicationNonVisualDrawingProperties(new PlaceholderShape() { Index = 1 }));
    bodyShape.ShapeProperties = new ShapeProperties();

    // Specify the text of the body shape.
    bodyShape.TextBody = new TextBody(new Drawing.BodyProperties(),
            new Drawing.ListStyle(),
            new Drawing.Paragraph());
    // Create the slide part for the new slide.
    SlidePart slidePart = presentationPart.AddNewPart<SlidePart>();
    
    // Assign the new slide to the new slide part
    slidePart.Slide = slide;

    // Modify the slide ID list in the presentation part.
    // The slide ID list should not be null.
    SlideIdList? slideIdList = presentationPart.Presentation.SlideIdList;

    // Find the highest slide ID in the current list.
    uint maxSlideId = 1;
    SlideId? prevSlideId = null;

    OpenXmlElementList slideIds = slideIdList?.ChildElements ?? default;

    foreach (SlideId slideId in slideIds)
    {
        if (slideId.Id is not null && slideId.Id > maxSlideId)
        {
            maxSlideId = slideId.Id;
        }

        position--;
        if (position == 0)
        {
            prevSlideId = slideId;
        }

    }

    maxSlideId++;

    // Get the ID of the previous slide.
    SlidePart lastSlidePart;

    if (prevSlideId is not null && prevSlideId.RelationshipId is not null)
    {
        lastSlidePart = (SlidePart)presentationPart.GetPartById(prevSlideId.RelationshipId!);
    }
    else
    {
        string? firstRelId = ((SlideId)slideIds[0]).RelationshipId;
        // If the first slide does not contain a relationship ID, throw an exception.
        if (firstRelId is null)
        {
            throw new ArgumentNullException(nameof(firstRelId));
        }

        lastSlidePart = (SlidePart)presentationPart.GetPartById(firstRelId);
    }

    // Use the same slide layout as that of the previous slide.
    if (lastSlidePart.SlideLayoutPart is not null)
    {
        slidePart.AddPart(lastSlidePart.SlideLayoutPart);
    }

    // Insert the new slide into the slide list after the previous slide.
    SlideId newSlideId = slideIdList!.InsertAfter(new SlideId(), prevSlideId);
    newSlideId.Id = maxSlideId;
    newSlideId.RelationshipId = presentationPart.GetIdOfPart(slidePart);

    return slidePart;
}

另请参阅