我正在尝试使用OpenXML从.docx文件中删除段落(我使用一些占位符文本从docx模板文件生成),但每当我删除段落时它会破坏我正在使用的foreach循环迭代低谷。
MainDocumentPart mainpart = doc.MainDocumentPart;
IEnumerable<OpenXmlElement> elems = mainPart.Document.Body.Descendants();
foreach(OpenXmlElement elem in elems){
if(elem is Text && elem.InnerText == "##MY_PLACE_HOLDER##")
{
Run run = (Run)elem.Parent;
Paragraph p = (Paragraph)run.Parent;
p.RemoveAllChildren();
p.Remove();
}
}
这样可以删除我的占位符和它所在的段落,但是foreach循环停止迭代。在foreach循环中我需要做更多的事情。
这是 ok 使用OpenXML删除C#中的段落和为什么我的foreach循环停止或如何使其不停止?感谢。
答案 0 :(得分:12)
这是“万圣节问题”,之所以这么称呼,是因为一些开发者在万圣节时就注意到了它,它们看起来很怪异。这是使用声明性代码(查询)同时使用命令式代码(删除节点)的问题。如果你考虑一下,你正在迭代一个链表,如果你开始删除链表中的节点,你就完全弄乱了迭代器。避免此问题的一种更简单的方法是在列表中“实现”查询结果,然后您可以遍历列表,并随意删除节点。以下代码的唯一区别是它在调用Descendants轴后调用ToList。
MainDocumentPart mainpart = doc.MainDocumentPart;
IEnumerable<OpenXmlElement> elems = mainPart.Document.Body.Descendants().ToList();
foreach(OpenXmlElement elem in elems){
if(elem is Text && elem.InnerText == "##MY_PLACE_HOLDER##")
{
Run run = (Run)elem.Parent;
Paragraph p = (Paragraph)run.Parent;
p.RemoveAllChildren();
p.Remove();
}
}
但是,我必须注意,我在您的代码中看到了另一个错误。没有什么可以阻止Word将该文本节点从多个运行拆分为多个文本元素。虽然在大多数情况下,您的代码可以正常工作,但是您或用户迟早会采取某些操作(例如选择一个字符,并且不小心点击功能区上的粗体按钮),然后您的代码将不再有效。
如果您真的想在文本级别工作,那么您需要使用代码,例如我在此屏幕中所引入的内容:http://openxmldeveloper.org/blog/b/openxmldeveloper/archive/2011/08/04/introducing-textreplacer-a-new-class-for-powertools-for-open-xml.aspx
事实上,我相信你可能会逐字地使用该代码来处理你的用例。
另一种更灵活,更强大的方法详见:
虽然该屏幕播放是关于PresentationML的,但相同的原则适用于WordprocessingML。
但更好的是,鉴于您使用的是WordprocessingML,就是使用内容控件。有关文档生成的一种方法,请参阅:
http://ericwhite.com/blog/map/generating-open-xml-wordprocessingml-documents-blog-post-series/
有关一般使用内容控件的大量信息,请参阅:
http://www.ericwhite.com/blog/content-controls-expanded
-Eric
答案 1 :(得分:2)
首先必须使用两个循环来存储要删除的项目,然后再删除项目。 像这样的东西:
List<Paragraph> paragraphsToDelete = new List<Paragraph>();
foreach(OpenXmlElement elem in elems){
if(elem is Text && elem.InnerText == "##MY_PLACE_HOLDER##")
{
Run run = (Run)elem.Parent;
Paragraph p = (Paragraph)run.Parent;
paragraphsToDelete.Add(p);
}
}
foreach (var p in paragraphsToDelete)
{
p.RemoveAllChildren();
p.Remove();
}
答案 2 :(得分:0)
Dim elems As IEnumerable(Of OpenXmlElement) = MainPart.Document.Body.Descendants().ToList()
For Each elem As OpenXmlElement In elems
If elem.InnerText.IndexOf("fullname") > 0 Then
elem.RemoveAllChildren()
End If
Next