C#openxml删除段落

时间:2012-03-26 16:20:40

标签: 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();
    }
}

这样可以删除我的占位符和它所在的段落,但是foreach循环停止迭代。在foreach循环中我需要做更多的事情。

这是 ok 使用OpenXML删除C#中的段落和为什么我的foreach循环停止或如何使其不停止?感谢。

3 个答案:

答案 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

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

另一种更灵活,更强大的方法详见:

http://openxmldeveloper.org/blog/b/openxmldeveloper/archive/2011/06/13/open-xml-presentation-generation-using-a-template-presentation.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
相关问题