【问题标题】:How I can replace text of bookmark when bookmark is many paragraphs (lines) with openxml and c#当书签有很多段落(行)时,如何用openxml和c#替换书签文本
【发布时间】:2018-06-27 13:59:10
【问题描述】:

我尝试使用 openxml 替换书签的文本。它仅适用于每个段落的第一行和单行。

我的代码:

foreach (BookmarkStart bookMarkStart in wordprocessingDocument.MainDocumentPart.RootElement.Descendants<BookmarkStart>())
{
    if (bookMarkStart.Name == "signet")
    {
        OpenXmlElement elem = bookMarkStart.NextSibling();

        while (elem != null && !(elem is BookmarkEnd))
        {
            OpenXmlElement nextElem = elem.NextSibling();
            elem.Remove();
            elem = nextElem;
        }

        bookMarkStart.Parent.InsertAfter<Run>(new Run(new Text("teeeest")), bookMarkStart);
    }
}

以下是使用这些工具的 xml 文件。这里我有两段,但只有第一个被替换,我用id=0 使用书签,第二个是自动添加的

<w:body>
    <w:p w:rsidR="0028616D" w:rsidRDefault="005537D9">
      <w:bookmarkStart w:name="signet" w:id="0" />
      <w:r>
        <w:t>Test1</w:t>
      </w:r>
    </w:p>
    <w:p w:rsidR="005537D9" w:rsidRDefault="005537D9">
      <w:r>
        <w:t>Test2</w:t>
      </w:r>
      <w:bookmarkStart w:name="_GoBack" w:id="1" />
      <w:bookmarkEnd w:id="0" />
      <w:bookmarkEnd w:id="1" />
    </w:p>
    <w:sectPr w:rsidR="005537D9">
      <w:pgSz w:w="11906" w:h="16838" />
      <w:pgMar w:top="1417" w:right="1417" w:bottom="1417" w:left="1417" w:header="708" w:footer="708" w:gutter="0" />
      <w:cols w:space="708" />
      <w:docGrid w:linePitch="360" />
    </w:sectPr>
 </w:body>

【问题讨论】:

  • 能否请您包含相关的 XML,以便我们可以看到书签的所有“部分”(标签)?
  • @CindyMeister 在哪里可以找到 xml 文件?
  • 例如,Open XML SDK 生产力工具可以为您提供。真的,我只是对与书签相关的标签感兴趣。书签有一个开始标签和一个结束标签——你需要两者来获取整个内容,以及层次结构中它们周围的内容。书签可能很棘手,这就是为什么内容控件在使用 Open XML 时会更好...
  • @CindyMeister 我添加了 xml 文件,你现在可以看到了吗?
  • 其他人可以帮忙吗?

标签: c# asp.net ms-word openxml bookmarks


【解决方案1】:

下面的代码正好适用于您显示的 XML 构造:书签在段落的开头和结尾处开始和结束在一个段落中。还有许多其他变体,必须明确地满足每一种变体。

书签由起点和终点组成。您需要两者才能获取内容。

由于一个文档可以有多个书签,并且书签可以重叠,因此有必要获取书签的Id 以确定哪个端点与起始点匹配。该名称仅出现在 BookmarkStart 元素中。只有Id 用于开始和结束元素。

有必要确定书签起点和终点的位置(在哪种结构中),因为这提供了有关父元素、兄弟元素和子元素可以是什么的信息。对于这个特定的用例,因为书签开始和结束都在段落内,所以两者的父级都是 Paragraph 元素。下面的代码通过检查Parent.LocalName 来确定这一点。

在这种情况下,开始点和结束点的父段落都已确定。为了编辑书签中所有段落的内容,创建了一个List;起点的父段落被添加到它。创建一个额外的Paragraph 对象用于检查下一个同级段落,并检查书签端点。只要书签结尾不在下一个同级段落的对象中,就会执行while 循环;下一个兄弟姐妹被添加到List。

一旦直到书签结尾的所有段落都包含在List 中,List 就会循环以替换每个段落中的文本。第一个Run 被复制以保留基本的段落格式。然后删除所有Run 和Text 元素,复制的Run 将附加新文本。

最后,书签end设置为最后一段的末尾。

    private void btnReplaceBookmarkText_Click(object sender, EventArgs e)
    {
        string fileNameDoc = "path name";
        string bkmName = "signet";
        string bkmID = "";
        string parentTypeStart = "";
        string parentTypeEnd = "";
        using (WordprocessingDocument pkgDoc = WordprocessingDocument.Open(fileNameDoc, true))
        {
            Body body = pkgDoc.MainDocumentPart.Document.Body;
            BookmarkStart bkmStart = body.Descendants<BookmarkStart>().Where(bkm => bkm.Name == bkmName).FirstOrDefault();
            bkmID = bkmStart.Id;
            BookmarkEnd bkmEnd = body.Descendants<BookmarkEnd>().Where(bkm => bkm.Id == bkmID).FirstOrDefault();
            parentTypeStart = bkmStart.Parent.LocalName;
            parentTypeEnd = bkmEnd.Parent.LocalName;
            int counter = 0;
            if (parentTypeStart == "p" && parentTypeEnd == "p") 
            { //bookmark starts at a paragraph and ends within a paragraph
                Paragraph bkmParaStart = (Paragraph) bkmStart.Parent;
                Paragraph bkmParaEnd = (Paragraph) bkmEnd.Parent;
                Paragraph bkmParaNext = (Paragraph) bkmParaStart; 
                List<Paragraph> paras = new List<Paragraph>();
                paras.Add(bkmParaStart);

                BookmarkEnd x = bkmParaNext.Descendants<BookmarkEnd>().Where(bkm => bkm.Id == bkmID).FirstOrDefault();
                while (x==null) 
                {
                    Paragraph nextPara = (Paragraph) bkmParaNext.NextSibling();
                    if (nextPara != null)
                    {
                        paras.Add(nextPara);
                        bkmParaNext = (Paragraph)nextPara.Clone();
                        x = bkmParaNext.Descendants<BookmarkEnd>().Where(bkm => bkm.Id == bkmID).FirstOrDefault();
                    }
                }
                foreach (Paragraph para in paras)
                {
                    string t = "changed string once more " + counter;
                    Run firstRun = para.Descendants<Run>().FirstOrDefault();
                    Run newRun = (Run) firstRun.Clone();
                    newRun.RemoveAllChildren<Text>();
                    para.RemoveAllChildren<Run>();
                    para.RemoveAllChildren<Text>();
                    para.AppendChild<Run>(newRun).AppendChild<Text>(new Text(t));
                }
                //After replacing the runs and text the bookmark is at the beginning
                //of the paragraph, we want it at the end
                BookmarkEnd newBkmEnd = new BookmarkEnd() { Id = bkmID };
                Paragraph p = paras.Last<Paragraph>();
                p.Descendants<BookmarkEnd>().Where(bkm => bkm.Id==bkmID).FirstOrDefault().Remove();
                p.Append(newBkmEnd);
            }
        }  
    }

注意: 由于我更熟悉 Word 对象模型而不是 XML,因此代码可能更优化,但它对我有用。

【讨论】:

    猜你喜欢
    • 2019-02-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-24
    • 2015-10-19
    • 1970-01-01
    • 1970-01-01
    • 2015-07-13
    相关资源
    最近更新 更多