如何在修订模式下删除Word文档的段落和Run并保留修订痕迹?
问题解答:在OpenXML中标记Word文档内容为删除(修订模式)
可行性确认
完全可行。通过OpenXML可以实现标记段落或Run为删除,保存后文档中的删除内容会显示删除线,用户接受所有修订时,标记的内容会被彻底移除,完全符合Word修订功能的标准行为。
原代码问题分析
你之前的代码存在两个核心问题:
- 直接将
Run元素添加到Deleted中,而Deleted的合法子元素是DeletedRun(对应<w:delR>),并非Run,因此触发"Non-composite elements do not have child elements"错误。 - 生成的
w:del元素未包裹要删除的内容,只是作为段落的子元素存在,导致删除标记未正确关联到目标文本。
正确示例代码
示例1:标记整个段落为删除
using (var document = WordprocessingDocument.Open(@"C:\Data\Test.docx", true)) { var mainPart = document.MainDocumentPart; // 确保文档开启修订模式 DocumentSettingsPart settingsPart = mainPart.DocumentSettingsPart ?? mainPart.AddNewPart<DocumentSettingsPart>(); Settings settings = settingsPart.Settings ?? new Settings(); if (settings.Elements<TrackRevisions>().FirstOrDefault() == null) { TrackRevisions trackRevisions = new TrackRevisions { Val = new DocumentFormat.OpenXml.OnOffValue(true) }; settings.AppendChild(trackRevisions); settings.Save(); } // 选择要删除的目标段落(此处以第一个段落为例,可根据实际需求修改选择逻辑) var targetParagraph = mainPart.Document.Body.Elements<Paragraph>().FirstOrDefault(); if (targetParagraph != null) { Deleted deleted = new Deleted { Author = "Author Name", Date = DateTime.Now }; // 将段落所有子元素克隆并移动到Deleted中,再移除原元素 foreach (var child in targetParagraph.Elements().ToList()) { deleted.AppendChild(child.CloneNode(true)); child.Remove(); } targetParagraph.AppendChild(deleted); } mainPart.Document.Save(); }
示例2:标记段落中的指定Run为删除
using (var document = WordprocessingDocument.Open(@"C:\Data\Test.docx", true)) { var mainPart = document.MainDocumentPart; // 确保文档开启修订模式 DocumentSettingsPart settingsPart = mainPart.DocumentSettingsPart ?? mainPart.AddNewPart<DocumentSettingsPart>(); Settings settings = settingsPart.Settings ?? new Settings(); if (settings.Elements<TrackRevisions>().FirstOrDefault() == null) { TrackRevisions trackRevisions = new TrackRevisions { Val = new DocumentFormat.OpenXml.OnOffValue(true) }; settings.AppendChild(trackRevisions); settings.Save(); } // 选择目标段落和要删除的Run(此处以第一个段落的第一个Run为例) var targetParagraph = mainPart.Document.Body.Elements<Paragraph>().FirstOrDefault(); if (targetParagraph != null) { var targetRun = targetParagraph.Elements<Run>().FirstOrDefault(); if (targetRun != null) { Deleted deleted = new Deleted { Author = "Author Name", Date = DateTime.Now }; // 创建DeletedRun并克隆原Run的内容 DeletedRun deletedRun = new DeletedRun(); foreach (var child in targetRun.Elements().ToList()) { deletedRun.AppendChild(child.CloneNode(true)); child.Remove(); } deleted.AppendChild(deletedRun); targetParagraph.AppendChild(deleted); // 删除空的原Run(可选,若保留则原Run会为空,不影响显示) targetRun.Remove(); } } mainPart.Document.Save(); }
生成的正确XML结构示例(删除整个段落)
<w:p> <w:del w:author="Author Name" w:date="2024-05-20T10:00:00"> <w:bookmarkStart w:name="_GoBack" w:id="0"/> <w:bookmarkEnd w:id="0"/> <w:r> <w:t>This is a sentence</w:t> </w:r> </w:del> </w:p>
关键注意事项
- 必须使用
Deleted元素包裹要删除的内容,确保删除标记与目标文本关联。 - 处理Run时,需通过
DeletedRun作为Deleted的子元素,不能直接添加原始Run。 - 修订模式的设置必须调用
settings.Save(),否则可能无法生效。
内容的提问来源于stack exchange,提问作者Mahesh
相关产品推荐
相关产品推荐

