C#openxml删除段落

edi*_*n-m 11 c# openxml

我试图使用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();
    }
}
Run Code Online (Sandbox Code Playgroud)

这工作,删除我的占位符和它所在的段落,但foreach循环停止迭代.在foreach循环中我需要做更多的事情.

这是正常使用的OpenXML和删除在C#中的段落方式为什么我的foreach循环停止或如何使它不会停止?谢谢.

Eri*_*ite 13

这是"万圣节问题",之所以这么称呼,是因为一些开发人员在万圣节时就注意到了它,它们看起来很怪异.这是使用声明性代码(查询)同时使用命令式代码(删除节点)的问题.如果你考虑一下,你正在迭代一个链表,如果你开始删除链表中的节点,你就完全弄乱了迭代器.避免此问题的一种更简单的方法是在列表中"实现"查询结果,然后您可以遍历列表,并随意删除节点.以下代码的唯一区别是它在调用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(); 
    } 
} 
Run Code Online (Sandbox Code Playgroud)

但是,我必须注意,我在您的代码中看到了另一个错误.没有什么可以阻止Word将该文本节点从多个运行拆分为多个文本元素.虽然在大多数情况下,您的代码可以正常工作,但是您或用户迟早会采取某些操作(例如选择一个字符,并且不小心点击功能区上的粗体按钮),然后您的代码将不再有效.

如果你真的想在文本级别工作,那么你需要使用代码,比如我在这个屏幕上演示的代码:http: //openxmldeveloper.org/blog/b/openxmldeveloper/archive/2011/08/04/引入-textreplacer-A-新级换电动工具换开xml.aspx

事实上,我相信你可能会逐字地使用该代码来处理你的用例.

另一种更灵活,更强大的方法详述如下:

http://openxmldeveloper.org/blog/b/openxmldeveloper/archive/2011/06/13/open-xml-presentation-generation-using-a-template-presentation.aspx

虽然该screen-cast是关于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


Den*_*sky 5

您必须先使用两个循环来存储要删除的项目,然后再使用两个循环来删除项目。像这样:

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();
}
Run Code Online (Sandbox Code Playgroud)