从演示文稿中删除幻灯片
本主题演示如何使用 Open XML SDK for Office 以编程方式从演示文稿中删除幻灯片。 它还演示如何从任何可能存在的自定义放映中删除对幻灯片的所有引用。 若要删除演示文稿文件中的特定幻灯片,您首先需要知道演示文稿中幻灯片的数目。 因此,本操作方法中的代码分为两部分。 第一部分统计幻灯片数目,第二部分删除特定索引处的幻灯片。
备注
[!注释] 从更加复杂的演示文稿(如那些包含大纲视图设置的演示文稿)中删除幻灯片可能需要其他步骤(举例来说)。
在 Open XML SDK 中, PresentationDocument 类表示演示文稿文档包。 若要使用演示文稿文档,首先创建 PresentationDocument 类的实例,然后使用该实例。 若要从文档创建类实例,请调用 Open 方法重载之一。 本主题中的代码使用 PresentationDocument.Open (String,Boolean) 方法,该方法采用文件路径作为第一个参数来指定要打开的文件,使用布尔值作为第二个参数来指定文档是否可编辑。 将此第二个参数设置为 false 将打开文件进行只读访问;如果要打开文件进行读/写访问,可将此参数设置为 true。 本主题中的代码打开文件两次,一次用于统计幻灯片数,一次用于删除特定幻灯片。 计算演示文稿中的幻灯片数时,最好打开文件进行只读访问,以防止意外写入文件。 下面的 using 语句打开文件进行只读访问。 在此代码示例中, presentationFile 参数是一个字符串,表示要从中打开文档的文件的路径。
// Open the presentation as read-only.
using (PresentationDocument presentationDocument = PresentationDocument.Open(presentationFile, false))
{
// Insert other code here.
}
若要从演示文稿文件中删除幻灯片,请打开文件进行读/写访问,如以下的 using 语句所示。
// Open the source document as read/write.
using (PresentationDocument presentationDocument = PresentationDocument.Open(presentationFile, true))
{
// Place other code here.
}
using 语句提供典型 .Open, .Save, .Close 序列的建议备选序列。 它确保在遇到右大括号时会自动调用 Dispose 方法(Open XML SDK 用来清理资源的内部方法)。 using 语句后面的块为 using 语句中创建或指定的对象设定范围,在此示例中这个范围就是 presentationDocument。
PresentationML 文档的基本文档结构包含大量部件,在这些部件中,主部件是包含演示文稿定义的部件。 ISO/IEC 29500(该链接可能指向英文页面) 规范中的以下文本介绍了 PresentationML 包的整体形式。
PresentationML 包的主部件以演示文稿根元素开头。 该元素包含演示文稿,演示文稿又引用幻灯片 列表、幻灯片母版 列表、备注母版 列表和讲义母版 列表。 幻灯片列表指的是演示文稿中的所有幻灯片;幻灯片母版列表指的是演示文稿中使用的全部幻灯片母版;备注母版包含有关备注页格式的信息;讲义母版描述讲义的外观。
讲义 是可提供给访问群体 的一组打印的幻灯片。
除了文本和图形,每个幻灯片还可以包含注释 和备注,可以具有布局,并且可以是一个或多个自定义演示文稿 的组成部分。 注释是供维护演示文稿幻灯片平台的人员参考的批注。 备注是供演示者或访问群体参考的提醒信息或一段文字。
PresentationML 文档可以包括以下其他功能:幻灯片之间的动画、音频、视频和切换效果。
PresentationML 文档不会存储为单个部件中的一个大型正文。 而实现某些功能组合的元素会存储在各个部件中。 例如,文档中的所有注释都存储在一个注释部件中,而每个幻灯片都有自己的部件。
© ISO/IEC29500: 2008.
以下 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 元素的强类型类创建文档结构和内容。 可以在 DocumentFormat.OpenXml.Presentation 命名空间中找到这些类。 下表列出了 sld、 sldLayout、 sldMaster 和 notesMaster 元素所对应类的类名称。
PresentationML 元素 | Open XML SDK 类 | 说明 |
---|---|---|
Sld | Slide | 演示文稿幻灯片。 它是 SlidePart 的根元素。 |
sldLayout | SlideLayout | 幻灯片版式。 它是 SlideLayoutPart 的根元素。 |
sldMaster | SlideMaster | 幻灯片母版。 它是 SlideMasterPart 的根元素。 |
notesMaster | NotesMaster | 备注母版(或讲义母版)。 它是 NotesMasterPart 的根元素。 |
示例代码由 CountSlides 方法的两个重载组成。 第一个重载使用 string 参数,第二个重载使用 PresentationDocument 参数。 在第一个 CountSlides 方法中,示例代码在 using 语句中打开演示文稿文档。 然后它将 PresentationDocument 对象传递给第二个 CountSlides 方法,该方法返回一个整数,表示演示文稿中幻灯片的数目。
// Pass the presentation to the next CountSlides method
// and return the slide count.
return CountSlides(presentationDocument);
在第二个 CountSlides 方法中,代码验证传入的 PresentationDocument 对象是否为 null,如果不为 null,则从 PresentationDocument 对象中获取 PresentationPart 对象。 通过使用 SlideParts,代码获取并返回 slideCount。
// Check for a null document object.
if (presentationDocument == null)
{
throw new ArgumentNullException("presentationDocument");
}
int slidesCount = 0;
// Get the presentation part of document.
PresentationPart presentationPart = presentationDocument.PresentationPart;
// Get the slide count from the SlideParts.
if (presentationPart != null)
{
slidesCount = presentationPart.SlideParts.Count();
}
// Return the slide count to the previous method.
return slidesCount;
用于删除幻灯片的代码使用 DeleteSlide 方法的两个重载。 第一个重载的 DeleteSlide 方法采用两个参数:一个表示演示文稿文件名和路径的字符串,和一个表示要删除的幻灯片从零开始的索引位置的整数。 此方法将打开演示文稿文件进行读/写访问,获取 PresentationDocument 对象,然后将此对象和索引号传递给下一个执行删除操作的重载 DeleteSlide 方法。
// Get the presentation object and pass it to the next DeleteSlide method.
public static void DeleteSlide(string presentationFile, int slideIndex)
{
// Open the source document as read/write.
using (PresentationDocument presentationDocument = PresentationDocument.Open(presentationFile, true))
{
// Pass the source document and the index of the slide to be deleted to the next DeleteSlide method.
DeleteSlide(presentationDocument, slideIndex);
}
}
第二个重载的 DeleteSlide 方法的第一部分使用 CountSlides 方法获取演示文稿中的幻灯片数目。 然后获取演示文稿中幻灯片 ID 的列表,标识幻灯片列表中的指定幻灯片,并从幻灯片列表中移除该幻灯片。
// Delete the specified slide from the presentation.
public static void DeleteSlide(PresentationDocument presentationDocument, int slideIndex)
{
if (presentationDocument == null)
{
throw new ArgumentNullException("presentationDocument");
}
// Use the CountSlides sample to get the number of slides in the presentation.
int slidesCount = CountSlides(presentationDocument);
if (slideIndex < 0 || slideIndex >= slidesCount)
{
throw new ArgumentOutOfRangeException("slideIndex");
}
// Get the presentation part from the presentation document.
PresentationPart presentationPart = presentationDocument.PresentationPart;
// Get the presentation from the presentation part.
Presentation presentation = presentationPart.Presentation;
// Get the list of slide IDs in the presentation.
SlideIdList slideIdList = presentation.SlideIdList;
// Get the slide ID of the specified slide
SlideId slideId = slideIdList.ChildElements[slideIndex] as SlideId;
// Get the relationship ID of the slide.
string slideRelId = slideId.RelationshipId;
// Remove the slide from the slide list.
slideIdList.RemoveChild(slideId);
第二个重载的 DeleteSlide 方法的下一部分从自定义放映中移除对已删除幻灯片的所有引用。 为此,它循环访问自定义放映列表并循环访问每个自定义放映中的幻灯片列表。 然后它声明并实例化幻灯片列表项的链接列表,并使用已删除幻灯片的关系 ID 查找对该幻灯片的引用。 它将这些引用添加到幻灯片列表项的列表中,然后从各自的自定义放映的幻灯片列表中移除每个这样的引用。
// Remove references to the slide from all custom shows.
if (presentation.CustomShowList != null)
{
// Iterate through the list of custom shows.
foreach (var customShow in presentation.CustomShowList.Elements<CustomShow>())
{
if (customShow.SlideList != null)
{
// Declare a link list of slide list entries.
LinkedList<SlideListEntry> slideListEntries = new LinkedList<SlideListEntry>();
foreach (SlideListEntry slideListEntry in customShow.SlideList.Elements())
{
// Find the slide reference to remove from the custom show.
if (slideListEntry.Id != null && slideListEntry.Id == slideRelId)
{
slideListEntries.AddLast(slideListEntry);
}
}
// Remove all references to the slide from the custom show.
foreach (SlideListEntry slideListEntry in slideListEntries)
{
customShow.SlideList.RemoveChild(slideListEntry);
}
}
}
}
最后,代码保存修改的演示文稿,并删除已删除幻灯片的幻灯片部件。
// Save the modified presentation.
presentation.Save();
// Get the slide part for the specified slide.
SlidePart slidePart = presentationPart.GetPartById(slideRelId) as SlidePart;
// Remove the slide part.
presentationPart.DeletePart(slidePart);
}
以下是两个重载的方法 CountSlides 和 DeleteSlide 的完整示例代码。 若要使用该代码,可以使用以下调用作为示例来删除演示文稿文件"Myppt6.pptx"中位于索引 2 处的幻灯片。
DeleteSlide(@"C:\Users\Public\Documents\Myppt6.pptx", 2);
您还可以使用以下调用统计演示文稿中幻灯片的数目。
Console.WriteLine("Number of slides = {0}",
CountSlides(@"C:\Users\Public\Documents\Myppt6.pptx"));
最好在执行删除前和执行删除后均统计幻灯片的数目。
以下是使用 C# 和 Visual Basic 编写的完整示例代码。
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Presentation;
using System;
using System.Collections.Generic;
using System.Linq;
CountSlides(args[0]);
// Get the presentation object and pass it to the next CountSlides method.
static int CountSlides(string presentationFile)
{
// Open the presentation as read-only.
using (PresentationDocument presentationDocument = PresentationDocument.Open(presentationFile, false))
{
// Pass the presentation to the next CountSlide method
// and return the slide count.
return CountSlidesFromPresentation(presentationDocument);
}
}
// Count the slides in the presentation.
static int CountSlidesFromPresentation(PresentationDocument presentationDocument)
{
int slidesCount = 0;
// Get the presentation part of document.
PresentationPart presentationPart = presentationDocument.PresentationPart ?? presentationDocument.AddPresentationPart();
// Get the slide count from the SlideParts.
if (presentationPart is not null)
{
slidesCount = presentationPart.SlideParts.Count();
}
// Return the slide count to the previous method.
return slidesCount;
}
//
// Get the presentation object and pass it to the next DeleteSlide method.
static void DeleteSlide(string presentationFile, int slideIndex)
{
// Open the source document as read/write.
using (PresentationDocument presentationDocument = PresentationDocument.Open(presentationFile, true))
{
// Pass the source document and the index of the slide to be deleted to the next DeleteSlide method.
DeleteSlideFromPresentation(presentationDocument, slideIndex);
}
}
// Delete the specified slide from the presentation.
static void DeleteSlideFromPresentation(PresentationDocument presentationDocument, int slideIndex)
{
if (presentationDocument is null)
{
throw new ArgumentNullException("presentationDocument");
}
// Use the CountSlides sample to get the number of slides in the presentation.
int slidesCount = CountSlidesFromPresentation(presentationDocument);
if (slideIndex < 0 || slideIndex >= slidesCount)
{
throw new ArgumentOutOfRangeException("slideIndex");
}
// Get the presentation part from the presentation document.
PresentationPart? presentationPart = presentationDocument.PresentationPart;
// Get the presentation from the presentation part.
Presentation? presentation = presentationPart?.Presentation;
// Get the list of slide IDs in the presentation.
SlideIdList? slideIdList = presentation?.SlideIdList;
// Get the slide ID of the specified slide
SlideId? slideId = slideIdList?.ChildElements[slideIndex] as SlideId;
// Get the relationship ID of the slide.
string? slideRelId = slideId?.RelationshipId;
// If there's no relationship ID, there's no slide to delete.
if (slideRelId is null)
{
return;
}
// Remove the slide from the slide list.
slideIdList!.RemoveChild(slideId);
//
// Remove references to the slide from all custom shows.
if (presentation!.CustomShowList is not null)
{
// Iterate through the list of custom shows.
foreach (var customShow in presentation.CustomShowList.Elements<CustomShow>())
{
if (customShow.SlideList is not null)
{
// Declare a link list of slide list entries.
LinkedList<SlideListEntry> slideListEntries = new LinkedList<SlideListEntry>();
foreach (SlideListEntry slideListEntry in customShow.SlideList.Elements())
{
// Find the slide reference to remove from the custom show.
if (slideListEntry.Id is not null && slideListEntry.Id == slideRelId)
{
slideListEntries.AddLast(slideListEntry);
}
}
// Remove all references to the slide from the custom show.
foreach (SlideListEntry slideListEntry in slideListEntries)
{
customShow.SlideList.RemoveChild(slideListEntry);
}
}
}
}
// Save the modified presentation.
presentation.Save();
// Get the slide part for the specified slide.
SlidePart slidePart = (SlidePart)presentationPart!.GetPartById(slideRelId);
// Remove the slide part.
presentationPart.DeletePart(slidePart);
}