在 Open XML SDK 中的单词书签后插入 OpenXmlElement

2023-11-24

我可以使用以下代码访问我的Word文档中的书签:

var res = from bm in mainPart.Document.Body.Descendants<BookmarkStart>()
                              where bm.Name == "BookmarkName"
                              select bm;

现在我想在这个书签后面插入一个段落和一个表格。我怎么做? (示例代码将不胜感激)


Code

获得书签后,您可以访问其父元素并在其后添加其他项目。

using (WordprocessingDocument document = WordprocessingDocument.Open(@"C:\Path\filename.docx", true))
{
    var mainPart = document.MainDocumentPart;
    var res = from bm in mainPart.Document.Body.Descendants<BookmarkStart>()
              where bm.Name == "BookmarkName"
              select bm;
    var bookmark = res.SingleOrDefault();
    if (bookmark != null)
    {
        var parent = bookmark.Parent;   // bookmark's parent element

        // simple paragraph in one declaration
        //Paragraph newParagraph = new Paragraph(new Run(new Text("Hello, World!")));

        // build paragraph piece by piece
        Text text = new Text("Hello, World!");
        Run run = new Run(new RunProperties(new Bold()));
        run.Append(text);
        Paragraph newParagraph = new Paragraph(run);

        // insert after bookmark parent
        parent.InsertAfterSelf(newParagraph);

        var table = new Table(
        new TableProperties(
            new TableStyle() { Val = "TableGrid" },
            new TableWidth() { Width = 0, Type = TableWidthUnitValues.Auto }
            ),
            new TableGrid(
                new GridColumn() { Width = (UInt32Value)1018U },
                new GridColumn() { Width = (UInt32Value)3544U }),
        new TableRow(
            new TableCell(
                new TableCellProperties(
                    new TableCellWidth() { Width = 0, Type = TableWidthUnitValues.Auto }),
                new Paragraph(
                    new Run(
                        new Text("Category Name"))
                )),
            new TableCell(
                new TableCellProperties(
                    new TableCellWidth() { Width = 4788, Type = TableWidthUnitValues.Dxa }),
                new Paragraph(
                    new Run(
                        new Text("Value"))
                ))
        ),
        new TableRow(
            new TableCell(
                new TableCellProperties(
                    new TableCellWidth() { Width = 0, Type = TableWidthUnitValues.Auto }),
                new Paragraph(
                    new Run(
                        new Text("C1"))
                )),
            new TableCell(
                new TableCellProperties(
                    new TableCellWidth() { Width = 0, Type = TableWidthUnitValues.Auto }),
                new Paragraph(
                    new Run(
                        new Text("V1"))
                ))
        ));

        // insert after new paragraph
        newParagraph.InsertAfterSelf(table);
    }

    // close saves all parts and closes the document
    document.Close();
}

上面的代码应该可以做到。不过,我会解释一些特殊情况。

请注意,它将尝试在书签的父元素之后插入。如果您的书签恰好是表格内段落的一部分,您期望什么行为?是否应该在该表内紧随其后附加新的段落和表格?或者应该在那张桌子之后做?

您可能想知道为什么上述问题很重要。这完全取决于插入发生的位置。如果书签的父级位于表中,则当前上述代码将尝试将表放置在表中。没关系,但是由于 OpenXml 结构无效,可能会出现错误。原因是,如果插入的表格是原始表格 TableCell 中的最后一个元素,则需要在结束 TableCell 标记之后添加一个 Paragraph 元素。如果您尝试在 MS Word 中打开文档时发生此问题,您会立即发现此问题。

解决方案是确定您是否确实在表中执行插入操作。

为此,我们可以添加到上面的代码(在父变量之后):

    var parent = bookmark.Parent;   // bookmark's parent element

    // loop till we get the containing element in case bookmark is inside a table etc.
    // keep checking the element's parent and update it till we reach the Body
    var tempParent = bookmark.Parent;
    bool isInTable = false;
    while (tempParent.Parent != mainPart.Document.Body)
    {
        tempParent = tempParent.Parent;
        if (tempParent is Table && !isInTable)
            isInTable = true;
    }

    // ... 

    newParagraph.InsertAfterSelf(table);  // from above sample
    // if bookmark is in a table, add a paragraph after table
    if (isInTable)
        table.InsertAfterSelf(new Paragraph());

这应该可以防止错误发生并为您提供有效的 OpenXml。如果您对我之前的问题回答“是”并且想要在父表之后而不是像上面的代码那样在表内执行插入,则可以使用 while 循环的想法。如果是这种情况,上述问题将不再是问题,您可以用以下内容替换该循环和布尔值:

    var parent = bookmark.Parent;   // bookmark's parent element
    while (parent.Parent != mainPart.Document.Body)
    {
        parent = parent.Parent;
    }

这会不断重新分配父级,直到它成为主体级别的主要包含元素。因此,如果书签位于表中的段落中,则它将从 Paragraph 到 TableCell 到 TableRow 到 Table,然后停在那里,因为 Table 的父级是 Body。此时parent = Table元素,我们可以在它后面插入。

这应该涵盖一些不同的方法,具体取决于您的初衷。如果您在尝试后需要任何说明,请告诉我。

文件反射器

你可能想知道我是如何确定的GridColumn.Width价值观。我制作了一个表格并使用 Document Reflector 工具来获取它。当您安装 Open Xml SDK 时,生产力工具(如果您安装了它们)将位于C:\Program Files\Open XML Format SDK\V2.0\tools(或类似)。

了解 *.docx 格式(或任何 Open Xml 格式的文档)如何工作的最佳方法是使用 Document Reflector 工具打开现有文件。导航文档部分,然后找到要复制的项目。该工具向您显示用于生成整个文档的实际代码。您可以将此代码复制/粘贴到您的应用程序中以生成类似的结果。通常您可以忽略所有参考 ID;你必须看一看并尝试一下才能找到感觉。

正如我所提到的,上面的表代码改编自示例文档。我向 docx 添加了一个简单的表格,然后在工具中打开它,并复制了该工具生成的代码(我删除了一些额外的内容以清理它)。这给了我一个添加表格的工作示例。

当您想知道如何编写生成某些内容(例如格式化表格和带有样式的段落等)的代码时,它特别有用。

请查看此链接,获取有关 SDK 中包含的其他工具的屏幕截图和信息:Open XML SDK 2.0 简介.

代码片段

您可能还对 Open Xml 的代码片段感兴趣。有关片段列表,请检查这篇博文。您可以从这里下载它们:2007 Office System 示例:适用于 Visual Studio 2008 的 Open XML 格式 SDK 2.0 代码片段.

安装后,您可以从“工具”|“添加”。代码片段管理器菜单。选择 C# 作为语言,单击“添加”按钮,然后导航到PersonalFolder\Visual Studio 2008\代码片段\Visual C#\Open XML SDK 2.0 for Microsoft Office添加它们。在您的代码中,您可以右键单击并选择“插入片段”,然后选择您想要的片段。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 Open XML SDK 中的单词书签后插入 OpenXmlElement 的相关文章

随机推荐

  • iframe 背景图像在 Firefox 中显示正常,但在 IE 中显示不正常

    为什么IE在Iframe中不像firefox那样显示BG法师 我无权访问 iframed 页面 任何 CSS 或 javascript 解决方案 以及添加 CSS 样式background color transparent 对于 ifra
  • Django+Apache ModuleNotFoundError:没有名为“myproject”的模块

    我对 Apache2 4 mod wsgi Python3 6 在 win10 x64 上 有问题 当我尝试访问站点时 我在 Apache 中收到此错误 Wed Mar 22 22 52 19 162397 2017 wsgi error
  • Laravel Passport 自定义密码栏

    如何使用 Laravel 的 Passport 包来验证不同的密码列 如果我想从不同的 用户名 列进行身份验证 可以使用以下代码完成 public function findForPassport username return this
  • 使用一个代码库为非 Retina 和 Retina 显示器提供服务:用于在 iPhone 或 iOS 设备上缩放 HTML5 应用程序的布局和资源的框架?

    我们的目标是模拟开发人员可以使用本机 iOS 应用程序执行的操作 即使用基于单位的单一布局来适应 Retina 显示屏 640x960 和非 Retina 显示屏 320x480 所有 iOS 开发人员需要做的就是提供两组资源 一组用于 R
  • java ArrayList 的时间复杂度

    Is ArrayListjava中的数组还是列表 get 操作的时间复杂度是多少 O n or O 1 An ArrayList在Java中是一个List这是由一个支持array The get index 方法是恒定时间 O 1 手术 直
  • 无法访问 URL:HTTP/1.1 400 错误 URI

    我想通过 Composer 创建一个新的 Laravel 项目 但出现此错误 Composer Downloader TransportException The http packagist org p fideloper proxy 2
  • greendao 字符串主键 - 如何使用

    在 greendao 常见问题解答中 它说 从 greenDAO 开始 对字符串主键的支持有限 http greendao orm com documentation technical faq 我找不到任何地方说明如何执行此操作 我使用
  • foreach 块缺少结束符“}”

    我今天玩 Razor 很开心 你能看出这个观点有什么问题并解释为什么它是错误的吗 foreach var item in Model if item ID PreviousOrderId div class orderdetail div
  • 当没有更多工作要做时,防止 boost::asio::io_context 返回

    boost asio io context run 当没有待处理的工作时确实返回 我想避免这种行为 以便run 确实无限期地等待新作品 并且可以从另一个线程停止它 我想 这可以通过在io context 并通过调用cancel 当我们想要的
  • 如何为特定类型的所有实例实现我自己的自定义属性编辑器?

    我已经遵循了一些关于创建自定义属性编辑器对话框的教程 但是涉及的事情太多 我无法让它正常工作 我想要完成的是一个带有日期选择器 日历 时间选择器以及 确定 和 取消 按钮的自定义表单 该表单完全没有问题 但是我将如何实现它 以便我可以通过启
  • 解开嵌入式资源的困惑

    编辑 阅读 Tim Schmelter 的答案 1 然后使用此问题作为如何嵌入资源并在运行时访问它们的示例 嵌入式资源的主题经常出现 尤其是当人们询问如何在运行时访问嵌入式文件时 事情变得更加混乱 因为 Visual Studio 为您提供
  • 在 C++ 中打开并显示图像?

    基本上我正在自学 C 程序功能的一部分是打开和关闭指定的图像 我该怎么做呢 或者我会使用什么资源 Thanks 在 C 中 无需任何额外的库 您可以打开图像 但除了一堆二进制数据之外 不会有什么特别有用的东西 那么你必须使用你自己的解码器
  • 分散数组中的重复项

    来源 谷歌面试问题 编写一个例程以确保输入中的相同元素在输出中最大程度地分布 基本上 我们需要放置相同的元素 以这样的方式TOTAL传播是尽可能最大的 Example Input 1 1 2 3 2 3 Possible Output 1
  • Linq 列表中的部分匹配?

    我有一个需要在表中匹配的部分字符串列表 我正在使用谓词构建器 var predicate PredicateBuilder False
  • Mac 上的 RStudio Python 版本更改

    我的问题是 如何将 python 版本更改为 anaconda 3 5 并可以在 RStudio 中使用 anaconda 中安装的模块 我正在使用 RStudio 版本 0 99 891R版本3 2 3 2015 12 10 平台 x86
  • 保存文件时出现权限错误(沙箱)

    我正在尝试将文件保存到沙盒应用程序 OS X 中的路径 但到目前为止 几乎每次尝试保存时都会遇到错误 错误是 Error saving Error Domain NSCocoaErrorDomain Code 513 You don t h
  • 为 iOS Metal 中 MTLBuffer 使用的数据分配内存

    作为后续问题这个答案 我正在尝试用 Metal 中的内核函数替换 CPU 上运行的 for 循环 以并行化计算并提高性能 我的函数基本上是一个卷积 由于我反复收到输入数组值的新数据 数据源于AVCaptureSession 似乎使用newB
  • Java Comparator 给出要比较的属性的名称

    我的问题是这样的 我必须订购一个数据表 表的每一行都是一个存储在 List 中的对象 我们称之为 TableObject 每列数据都是该类的一个属性 通常是字符串 当用户单击任何列时 我必须执行典型的数据排序 所以我考虑将 List 更改为
  • gdb如何显示线程名称

    我的应用程序中创建了许多线程 当我执行命令 infothreads 时 一些线程名称在 gdb 中可见 而其他线程名称则不显示 如何获取所有线程名称本身而不是像 0xb7fe1424 这样的十六进制值 4 Thread 0xb68ffb70
  • 在 Open XML SDK 中的单词书签后插入 OpenXmlElement

    我可以使用以下代码访问我的Word文档中的书签 var res from bm in mainPart Document Body Descendants