使用documentformat.openxml从具有多个页面的Word模板创建新文档 [英] create a new document from word template with multiple pages using documentformat.openxml

查看:204
本文介绍了使用documentformat.openxml从具有多个页面的Word模板创建新文档的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个带有某些内容控件的Microsoft Word模板.它包含一个目录和一些其他信息.

I have a Microsoft Word template with some content controls. It contains a table of contents and some extra information.

在页面上,我设置了一些内容控件,希望从RESTful服务中插入新文本.如果RESTful服务返回数据(对象)数组,则需要基于Word模板在每个页面上复制信息.

On the page, I have set some content controls where I would like to inject a new text from a RESTful service. If the RESTful service returns an array of data (objects), I need to replicate the information on each page based on the Word template.

关于如何使用Open XML SDK(DocumentFormat.OpenXml)做到这一点的任何想法?

Any idea on how can I do that using the Open XML SDK (DocumentFormat.OpenXml)?

我找到了这个

I found this post here, which is great, but I don't know how can I apply the array of data to multiple pages from the same template.

那么,如何在新文档中从同一模板创建多个页面?数据以数组形式输入.

So, how can I create multiple pages from the same template in a new document? The data comes in as an array.

推荐答案

下面的示例代码(已经过单元测试,可以工作)完成了您要实现的目标.它基于对问题和假设的以下解释:

The sample code below (which is unit-tested and works) does what you are trying to achieve. It is based on the following interpretation of the question and assumptions:

  • 控件占位符"是指富文本内容控件",在Open XML语言中被称为块级结构化文档标签(SDT),因此由Open XML SDK中的SdtBlock类表示.
  • li>
  • 内容控件具有标签,这意味着相关的w:sdt元素具有诸如<w:tag="tagValue" />之类的孙元素.这些标签用于将从REST服务接收的数据链接到内容控件.
  • 数据以Dictionary<string, string>的形式提供,将标签值映射到内容控件文本(数据).
  • "Control place holders" means "Rich Text content controls", which are called block-level structured document tags (SDTs) in Open XML lingo and are thus represented by the SdtBlock class in the Open XML SDK.
  • The content controls have tags, meaning the relevant w:sdt elements have grandchild elements like <w:tag="tagValue" />. Those tags are used to link the data received from the REST service to the content controls.
  • The data is provided as a Dictionary<string, string>, mapping tag values to the content control text (data).

一般方法是对WordprocessingDocument的主文档部分执行纯功能转换. void WriteContentControls(WordprocessingDocument)方法包装最外面的纯函数转换object TransformDocument(OpenXmlElement).后者使用内部纯函数转换object TransformSdtBlock(OpenXmlElement, string).

The general approach is to perform a pure functional transformation of the main document part of your WordprocessingDocument. The void WriteContentControls(WordprocessingDocument) method wraps the outermost pure functional transformation object TransformDocument(OpenXmlElement). The latter uses an inner pure functional transformation object TransformSdtBlock(OpenXmlElement, string).

public class ContentControlWriter
{
    private readonly IDictionary<string, string> _contentMap;

    /// <summary>
    /// Initializes a new ContentControlWriter instance.
    /// </summary>
    /// <param name="contentMap">The mapping of content control tags to content control texts.
    /// </param>
    public ContentControlWriter(IDictionary<string, string> contentMap)
    {
        _contentMap = contentMap;
    }

    /// <summary>
    /// Transforms the given WordprocessingDocument by setting the content
    /// of relevant block-level content controls.
    /// </summary>
    /// <param name="wordDocument">The WordprocessingDocument to be transformed.</param>
    public void WriteContentControls(WordprocessingDocument wordDocument)
    {
        MainDocumentPart part = wordDocument.MainDocumentPart;
        part.Document = (Document) TransformDocument(part.Document);
    }

    private object TransformDocument(OpenXmlElement element)
    {
        if (element is SdtBlock sdt)
        {
            string tagValue = GetTagValue(sdt);
            if (_contentMap.TryGetValue(tagValue, out string text))
            {
                return TransformSdtBlock(sdt, text);
            }
        }

        return Transform(element, TransformDocument);
    }

    private static object TransformSdtBlock(OpenXmlElement element, string text)
    {
        return element is SdtContentBlock
            ? new SdtContentBlock(new Paragraph(new Run(new Text(text))))
            : Transform(element, e => TransformSdtBlock(e, text));
    }

    private static string GetTagValue(SdtElement sdt) => sdt
        .Descendants<Tag>()
        .Select(tag => tag.Val.Value)
        .FirstOrDefault();

    private static T Transform<T>(T element, Func<OpenXmlElement, object> transformation)
        where T : OpenXmlElement
    {
        var transformedElement = (T) element.CloneNode(false);
        transformedElement.Append(element.Elements().Select(e => (OpenXmlElement) transformation(e)));
        return transformedElement;
    }
}

即使细节有所不同(例如,有关如何将数据数组映射到特定内容控件的信息),这也应为实施特定解决方案提供足够的输入.此外,如果您不使用块级结构化文档标签(SdtBlock,富文本格式内容控件),而是使用内联级结构化文档标签(SdtRun,纯文本内容控件),则原理是相同的.代替SdtContentBlock实例中包含的Paragraph实例(w:p元素),您将在SdtContentRun实例中包含Run实例(w:r元素).

This should provide enough input for implementing your specific solution even if the details vary (e.g., regarding how you map your array of data to the specific content controls). Further, if you don't use block-level structured document tags (SdtBlock, Rich Text content controls) but but rather inline-level structured document tags (SdtRun, plain text content controls), the principle is the same. Instead of the Paragraph instances (w:p elements) contained in SdtContentBlock instances, you would have Run instances (w:r elements) contained in SdtContentRun instances.

更新2019-11-23:我的 CodeSnippets GitHub存储库包含 ContentControlWriter

Update 2019-11-23: My CodeSnippets GitHub repository contains the code for the ContentControlWriter and AltChunkAssemblyTests classes. The latter shows how the ContentControlWriter class can be used.

这篇关于使用documentformat.openxml从具有多个页面的Word模板创建新文档的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆