C#openxml删除段落
我试图使用OpenXML从.docx文件中删除段落(我使用一些占位符文本从docx模板文件生成),但每当我删除段落时,它就会中断我用来迭代的foreach循环C#openxml删除段落,c#,openxml,C#,Openxml,我试图使用OpenXML从.docx文件中删除段落(我使用一些占位符文本从docx模板文件生成),但每当我删除段落时,它就会中断我用来迭代的foreach循环 MainDocumentPart mainpart = doc.MainDocumentPart; IEnumerable<OpenXmlElement> elems = mainPart.Document.Body.Descendants(); foreach(OpenXmlElement elem in elems){
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();
}
}
MainDocumentPart mainpart=doc.MainDocumentPart;
IEnumerable elems=mainPart.Document.Body.subjects();
foreach(元素中的OpenXmlElement元素){
if(elem为Text&&elem.InnerText==“############”)
{
Run=(Run)elem.Parent;
段落p=(段落)run.Parent;
p、 移除所有儿童();
p、 删除();
}
}
这样做可以删除我的占位符和它所在的段落,但是foreach循环停止迭代。我需要在我的foreach循环中做更多的事情
使用OpenXML和删除C#中的段落是否合适?为什么我的foreach循环停止,或者如何使它不停止?谢谢。您必须使用两个循环,第一个循环存储要删除的项目,第二个循环删除项目。 大概是这样的:
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();
}
列表段落删除=新建列表();
foreach(元素中的OpenXmlElement元素){
if(elem为Text&&elem.InnerText==“############”)
{
Run=(Run)elem.Parent;
段落p=(段落)run.Parent;
删除.添加(p)段;
}
}
foreach(第段删除中的变量p)
{
p、 移除所有儿童();
p、 删除();
}
这就是所谓的“万圣节问题”,之所以这样称呼,是因为一些开发人员在万圣节前夜注意到了这一点,而且他们觉得这很可怕。这是同时使用声明性代码(查询)和命令性代码(删除节点)的问题。如果你仔细想想,你是在一个链表中迭代,如果你开始删除链表中的节点,你会把迭代器搞得一团糟。避免此问题的一种更简单的方法是在列表中“具体化”查询结果,然后可以在列表中迭代,并随意删除节点。以下代码中唯一的区别是,它在调用子体轴之后调用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();
}
}
MainDocumentPart mainpart=doc.MainDocumentPart;
IEnumerable elems=mainPart.Document.Body.subjects().ToList();
foreach(元素中的OpenXmlElement元素){
if(elem为Text&&elem.InnerText==“############”)
{
Run=(Run)elem.Parent;
段落p=(段落)run.Parent;
p、 移除所有儿童();
p、 删除();
}
}
但是,我必须注意,我在代码中看到了另一个bug。没有什么可以阻止Word从多次运行中将该文本节点拆分为多个文本元素。虽然在大多数情况下,您的代码可以正常工作,但您或用户迟早会采取一些措施(例如选择角色,并意外地点击功能区上的粗体按钮),然后您的代码将不再工作
如果您真的想在文本级别工作,那么您需要使用代码,如我在本屏幕演示中介绍的代码:
事实上,我相信,您可能可以使用该代码逐字处理您的用例
另一种更灵活、更强大的方法详见:
虽然这个屏幕投射是关于PresentationML的,但同样的原则也适用于WordprocessingML
但如果您使用的是WordprocessingML,那么更好的方法是使用内容控件。有关生成文档的一种方法,请参见:
有关一般使用内容控件的详细信息,请参阅:
-埃里克天哪,我真蠢。谢谢但为什么它会从循环中断裂呢?(如果有人知道,我会留些时间接受答案;sry不能投票,代表太低)谢谢。找到了另一个好的解决方案:实际上我已经完成了.ToList(),因为使用以前的解决方案时出现了一些其他复杂情况。另外,我知道word将其拆分为多个运行(这里的例子很糟糕),因此我的占位符没有“\ux”。我的占位符是硬编码的,所以尽管我知道内容控制的优势,但我没有使用它们,因为我对它们了解不够,并且项目进度很短(迷你)。谢谢你的回答,它很有洞察力,更完整。
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