我有一个带有某些内容控件的Microsoft Word模板。它包含一个目录和一些其他信息。
在页面上,我设置了一些内容控件,希望从RESTful服务中插入新文本。如果RESTful服务返回数据(对象)数组,则需要基于Word模板在每个页面上复制信息。
关于如何使用Open XML SDK(DocumentFormat.OpenXml
)做到这一点的任何想法?
编辑:
我在这里找到了这个 post ,但这很好,但是我不知道如何将数据数组应用于同一模板的多个页面。
那么,如何在新文档中从同一模板创建多个页面?数据作为数组输入。
答案 0 :(得分:2)
下面的示例代码(已通过单元测试并且可以工作)完成了您要实现的目标。它基于对问题和假设的以下解释:
SdtBlock
类表示。 w:sdt
元素具有诸如<w:tag="tagValue" />
之类的孙元素。这些标签用于将从REST服务接收的数据链接到内容控件。Dictionary<string, string>
的形式提供,将标签值映射到内容控制文本(数据)。一般方法是对WordprocessingDocument
的主文档部分执行纯功能转换。 void WriteContentControls(WordprocessingDocument)
方法包装最外面的纯函数转换object TransformDocument(OpenXmlElement)
。后者使用内部纯函数转换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
,纯文本内容控件),则原理是一样的代替Paragraph
实例中包含的w:p
实例(SdtContentBlock
元素),您将拥有Run
中包含的w:r
实例(SdtContentRun
元素)实例。
更新2019-11-23:我的CodeSnippets GitHub存储库包含ContentControlWriter和AltChunkAssemblyTests类的代码。后者显示了如何使用ContentControlWriter
类。