打开现有文档进行编辑
若要打开现有文档,您可以将 Word类实例化,如以下 using 语句所示。 为此,您可以使用Open(String, Boolean) 方法打开具有指定 fileName 的字处理文件,同时将布尔参数设置为 true 以允许编辑文档。
1 using (WordprocessingDocument wdDoc = WordprocessingDocument.Open(fileName, true)) 2 { 3 // Insert other code here. 4 }
using 语句提供了典型 .Open、.Save、.Close 序列的建议备选方法。它确保在遇到右大括号时会自动调用 Dispose 方法(Open XML SDK 用来清理资源的内部方法)。跟在 using 语句后面的代码块为 using 语句中创建或指定的对象(在本示例中为 wdDoc)设定范围。由于 Open XML SDK 中的 WordprocessingDocument 类会在其实现 System.IDisposable 的过程中自动保存和关闭对象,并且由于在您退出代码块时会自动调用 Dispose,因此只要您使用 using,就不必明确调用 Save 和 Close。
Word 文档的结构
Word文档的基本结构由 document 和 body 元素组成,后跟一个或多个块级元素,如表示段落的 p。一个段落包含一个或多个 r 元素。 r 代表一段连续文本,它是具有一组共同属性(如格式设置)的文本区域。一段连续文本包含一个或多个 t 元素。 t 元素包含一个文本区域。以下代码示例显示包含文本"Example text."的文档的 Word 标记。
1 <w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
<w:body>
<w:p>
<w:r>
<w:t>Example text.</w:t>
</w:r>
</w:p>
</w:body>
</w:document>
通过使用 Open XML SDK ,您可以利用 WordprocessingML 元素所对应的强类型类创建文档结构和内容。 可以在DocumentFormat.OpenXml.Wordprocessing 命名空间中找到这些类。 下表列出了 document、body、p、r 和 t 元素所对应类的类名称。
Word 元素 | Open XML SDK 类 | 说明 |
---|---|---|
document | 文件 | 主文档部件的根元素 |
body | 主体 | 块级结构(如段落、表格、批注和 ISO/IEC 29500 规范中指定的其他项)的容器。 |
p | 段落 | 段落。 |
r | 运行 | 一段连续文本。 |
t | 文本 | 文本范围。 |
ParagraphPropertiesChange 元素
在接受修订标记时,会通过删除现有文本或插入新文本来更改段落的属性。 在以下各节中,您将了解代码中用于更改段落内容的三个元素,即 <w: pPrChange\>
(段落属性的修订信息)、<w:del>
(删除的段落标记)和<w:ins>
(插入的表格行)元素。
ISO/IEC 29500 规范中的以下信息介绍了 ParagraphPropertiesChange 元素 ( pPrChange)。
pPrChange(段落属性的修订信息)
此元素指定 Word 文档中一组段落属性的单处修订的详细信息。
此元素按如下方式存储此修订:
-
此元素的子元素包含此修订之前应用于此段落的完整段落属性集。
-
此元素的属性包含有关此修订发生时间(换句话说,这些段落属性何时成为"先前的"段落属性集)的信息。
假设对 Word文档中的一个段落设置了居中格式,并且将此段落属性更改作为修订进行跟踪。那么,将使用以下 Word标记指定此修订。
1 <w:pPr> 2 <w:jc w:val="center"/> 3 <w:pPrChange w:id="0" w:date="01-01-2006T12:00:00" w:author="Samantha Smith"> 4 <w:pPr/> 5 </w:pPrChange> 6 </w:pPr>
该元素表明,Samantha Smith 在 2006 年 1 月 1 日对段落属性进行了修订,并且有关段落的先前段落属性集是空集(即, pPr pPrChange 元素下未明确提供段落属性)。
Deleted 元素
ISO/IEC 29500 规范中的以下信息介绍了 Deleted 元素 ( del)。
del(删除的段落标记)
此元素表明,应将 Word文档中界定段落结束位置的段落标记视为视为已作为跟踪修订的一部分删除(换句话说,此段落的内容不再由此段落标记界定,而是与下一段落合并在一起 但是不应将这些内容自动标记为已删除)。
假设存在一个由两个段落组成(每个段落由一个段落标记 ¶ 界定)的文档:
如果删除界定第一个段落结束位置的有形字符并将此更改作为修订进行跟踪,则将产生以下结果:
此修订使用以下 Word表示:
<w:p> <w:pPr> <w:rPr> <w:del w:id="0" … /> </w:rPr> </w:pPr> <w:r> <w:t>This is paragraph one.</w:t> </w:r> </w:p> <w:p> <w:r> <w:t>This is paragraph two.</w:t> </w:r> </w:p>
第一个段落标记的连续文本属性的 del 元素指定此段落标记已删除,并且已将此删除作为修订进行跟踪。
Inserted 元素
ISO/IEC 29500 规范中的以下信息介绍了 Inserted 元素 ( ins)。
ins(插入的表格行)
此元素指定应将父表格行视为插入的行,并且已将该插入操作作为修订进行跟踪。此设置不应表示有关此行中的表格单元格或其内容的任何修订状态(它们必须通过单独的修订标记表示),并且应该仅影响表格行本身。
假设存在一个包含两行两列的表格,并且已使用修订将第二行标记为已插入。此要求将使用以下 Word 指定:
<w:tbl> <w:tr> <w:tc> <w:p/> </w:tc> <w:tc> <w:p/> </w:tc> </w:tr> <w:tr> <w:trPr> <w:ins w:id="0" … /> </w:trPr> <w:tc> <w:p/> </w:tc> <w:tc> <w:p/> </w:tc> </w:tr> </w:tbl>
第二个表格行的表格行属性的 ins 元素表明,此行是插入行,并且已将此插入操作作为修订进行跟踪。
示例代码
在 using 语句中打开文档后,将 Body 类实例化,然后通过以下方式处理格式更改:创建 changes List 并从 List 中删除每项更改( w:pPrChange 元素),这与接受更改相同。
Body body = wdDoc.MainDocumentPart.Document.Body; // Handle the formatting changes. List<OpenXmlElement> changes = body.Descendants<ParagraphPropertiesChange>() .Where(c => c.Author.Value == authorName).Cast<OpenXmlElement>().ToList(); foreach (OpenXmlElement change in changes) { change.Remove(); }
然后通过以下方式处理删除:构造 deletions List 并从 List 中删除每个删除元素 (w:del),这与接受删除更改的过程类似。
// Handle the deletions. List<OpenXmlElement> deletions = body.Descendants<Deleted>() .Where(c => c.Author.Value == authorName).Cast<OpenXmlElement>().ToList(); deletions.AddRange(body.Descendants<DeletedRun>() .Where(c => c.Author.Value == authorName).Cast<OpenXmlElement>().ToList()); deletions.AddRange(body.Descendants<DeletedMathControl>() .Where(c => c.Author.Value == authorName).Cast<OpenXmlElement>().ToList()); foreach (OpenXmlElement deletion in deletions) { deletion.Remove(); }
最后,通过以下方式处理插入:构造 insertions List 并通过删除插入元素 (w:ins) 来插入新文本,这与接受插入的文本相同。
复制 // Handle the insertions. List<OpenXmlElement> insertions = body.Descendants<Inserted>() .Where(c => c.Author.Value == authorName).Cast<OpenXmlElement>().ToList(); insertions.AddRange(body.Descendants<InsertedRun>() .Where(c => c.Author.Value == authorName).Cast<OpenXmlElement>().ToList()); insertions.AddRange(body.Descendants<InsertedMathControl>() .Where(c => c.Author.Value == authorName).Cast<OpenXmlElement>().ToList()); Run lastInsertedRun = null; foreach (OpenXmlElement insertion in insertions) { // Found new content. // Promote them to the same level as node, and then delete the node. foreach (var run in insertion.Elements<Run>()) { if (run == insertion.FirstChild) { lastInsertedRun = insertion.InsertAfterSelf(new Run(run.OuterXml)); } else { lastInsertedRun = lastInsertedRun.Insertion.InsertAfterSelf(new Run(run.OuterXml)); } } insertion.RemoveAttribute("rsidR", "http://schemas.openxmlformats.org/wordprocessingml/2006/main"); insertion.RemoveAttribute("rsidRPr", "http://schemas.openxmlformats.org/wordprocessingml/2006/main"); insertion.Remove(); }
以下代码示例演示如何接受字处理文档中的全部修订。若要运行程序,可以调用 AcceptRevisions 方法以接受"word1.docx"文件中的修订,如以下示例所示。
1 string docName = @"C:\Users\Public\Documents\word1.docx"; 2 string authorName = "lin"; 3 AcceptRevisions(docName, authorName);
运行程序后,打开字处理文件以确保已接受所有修订标记。
完整示例代码
1 public static void AcceptRevisions(string fileName, string authorName) 2 { 3 // Given a document name and an author name, accept revisions. 4 using (WordprocessingDocument wdDoc = 5 WordprocessingDocument.Open(fileName, true)) 6 { 7 Body body = wdDoc.MainDocumentPart.Document.Body; 8 9 // Handle the formatting changes. 10 List<OpenXmlElement> changes = 11 body.Descendants<ParagraphPropertiesChange>() 12 .Where(c => c.Author.Value == authorName).Cast<OpenXmlElement>().ToList(); 13 14 foreach (OpenXmlElement change in changes) 15 { 16 change.Remove(); 17 } 18 19 // Handle the deletions. 20 List<OpenXmlElement> deletions = 21 body.Descendants<Deleted>() 22 .Where(c => c.Author.Value == authorName).Cast<OpenXmlElement>().ToList(); 23 24 deletions.AddRange(body.Descendants<DeletedRun>() 25 .Where(c => c.Author.Value == authorName).Cast<OpenXmlElement>().ToList()); 26 27 deletions.AddRange(body.Descendants<DeletedMathControl>() 28 .Where(c => c.Author.Value == authorName).Cast<OpenXmlElement>().ToList()); 29 30 foreach (OpenXmlElement deletion in deletions) 31 { 32 deletion.Remove(); 33 } 34 35 // Handle the insertions. 36 List<OpenXmlElement> insertions = 37 body.Descendants<Inserted>() 38 .Where(c => c.Author.Value == authorName).Cast<OpenXmlElement>().ToList(); 39 40 insertions.AddRange(body.Descendants<InsertedRun>() 41 .Where(c => c.Author.Value == authorName).Cast<OpenXmlElement>().ToList()); 42 43 insertions.AddRange(body.Descendants<InsertedMathControl>() 44 .Where(c => c.Author.Value == authorName).Cast<OpenXmlElement>().ToList()); 45 46 foreach (OpenXmlElement insertion in insertions) 47 { 48 // Found new content. 49 // Promote them to the same level as node, and then delete the node. 50 foreach (var run in insertion.Elements<Run>()) 51 { 52 if (run == insertion.FirstChild) 53 { 54 insertion.InsertAfterSelf(new Run(run.OuterXml)); 55 } 56 else 57 { 58 insertion.NextSibling().InsertAfterSelf(new Run(run.OuterXml)); 59 } 60 } 61 insertion.RemoveAttribute("rsidR", 62 "http://schemas.openxmlformats.org/wordprocessingml/2006/main"); 63 insertion.RemoveAttribute("rsidRPr", 64 "http://schemas.openxmlformats.org/wordprocessingml/2006/main"); 65 insertion.Remove(); 66 } 67 } 68 }