通过OpenXML

时间:2017-09-26 10:33:22

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

我有一个在IIS 7.5上运行的ASP.NET 4.5 Web表单应用程序。

我正在尝试从其中一个我自定义表单的页面生成一个word文档。

我上传了包含合并字段的word文档模板。 在后面的代码中,我想基于sql数据库查询填充合并字段。

对于某些合并字段,我需要插入多行文本。其中一些人甚至有子弹清单。这些文本片段我不能存储在sql中,因此我将它们添加到带有书签的单独word文档中。

所以,回顾一下:

Template.dotx - >包含合并字段

Data.docx - >包含已标记书签的文本片段。

我已经设法使用OpenXML替换Template.dotx中的合并字段,但我找不到将书签中的数据导入合并字段的方法。

这对Interop很有用,但是当我将它上传到服务器上时我遇到了问题,所以我切换到了OpenXML。

这是我到目前为止所尝试的:

private string GetBookmarkData(WordprocessingDocument secondWordDoc, string bookmarkKey)
        {
            string returnVal = "";
            foreach (BookmarkStart bookmarkStart in secondWordDoc.MainDocumentPart.RootElement.Descendants<BookmarkStart>())
            {
                if(bookmarkStart.Name == bookmarkKey)
                {
                    foreach(Run run in bookmarkStart.Parent.Descendants<Run>())
                    {
                        returnVal += run.Descendants<Text>().FirstOrDefault().Text + "<br/>";
                    }
                }
            }
            return returnVal;
        }


protected void PrintBtn_Click(object sender, EventArgs e)
{
            string mainTemplate = Server.MapPath("~/MyFolder/Template.dotx");
            string savePath = Server.MapPath("~/SaveFolder/Final.docx");

            File.Copy(mainTemplate, savePath);
            using(WordprocessingDocument firstDoc = WordprocessingDocument.Open(savePath, true))
            {
                using (WordprocessingDocument secondDoc = WordprocessingDocument.Open(Server.MapPath("~/MyFolder/Data.docx"), true))
                {
                    foreach (FieldCode field in firstDoc.MainDocumentPart.RootElement.Descendants<FieldCode>())
                    {
                        var fieldNameStart = field.Text.LastIndexOf(" MERGEFIELD", System.StringComparison.Ordinal);
                        String fieldText = field.InnerText;
                        if (fieldText.StartsWith(" MERGEFIELD"))
                        {
                            Int32 endMerge = fieldText.IndexOf("\\");
                            Int32 fieldNameLength = fieldText.Length - endMerge;
                            String fieldName = fieldText.Substring(11, endMerge - 11);
                            fieldName = fieldName.Trim();
                            string autoFill = "";

                                switch (fieldName)
                                {
                                    case "MergeField1":
                                        autoFill = mergefield_1;
                                        break;
                                    case "MergeField2":
                                        autoFill = mergefield_2;
                                        break;
                                    case "MergeField3":
                                        autoFill = GetBookmarkData(secondDoc, "Bookmark1");
                                        break;
                                    case "MergeField4":
                                        autoFill = GetBookmarkData(secondDoc, "Bookmark2");
                                        break;
                                    case "MergeField5":
                                        autoFill = GetBookmarkData(secondDoc, "Bookmark3");
                                        break;
                              }
                        }

                        foreach (Run run in firstDoc.MainDocumentPart.Document.Descendants<Run>())
                        {
                           foreach (Text txtFromRun in run.Descendants<Text>().Where(a => a.Text == "«" + fieldName + "»"))
                           {
                              txtFromRun.Text = autoFill;
                           }
                        }
                    }
                } 
            }

    firstDoc.ChangeDocumentType(WordprocessingDocumentType.Document);
    firstDoc.MainDocumentPart.Document.Save();
}

}

这是做什么的?

当我点击一个按钮时,我调用方法PrintBtn_Click。在做了一些SQL魔术(我没有包含在内)之后,我初始化了一些将填充每个合并字段的变量。此示例是一个简短的编辑版本。原来要大得多。使用此代码,我设法填充合并字段。它很棒。但是方法:`

string GetBookmarkData(WordprocessingDocument secondWordDoc, string bookmarkKey)`

没有真正按照预期行事。它应该进入Data.docx,从我指定的书签中检索所有文本。它只返回没有项目符号或奇怪格式的行。

我使用Interop使用相同的过程,我没有问题。如何使用OpenXML执行此操作?带子弹的行是否存储在不同的xml中?

我尝试检索BookmarkStart和BookmarkEnd之间的所有运行并从中获取文本。

感谢任何帮助。 谢谢!

更新

secondDoc实际上是Data.docx,看起来像这样:

Bookmark1

•   Text-Information 1 (This is just an example)
•   Text-Information 2 (This is just an example)
•   Text-Information 3 (This is just an example)
•   Text-Information 4 (This is just an example)

Bookmark2

This is a list of multiple items:
Item 1                              x.000,00 
Item 2                              x.000,00 
Item 3                              x.000,00 
Item 4                              x.000,00 
Item 5                              000,00 
This is the conclusion for this list.

Following is a list of other multiple items:
Item 1                              x.000,00 
Item 2                              x.000,00 
Item 3                              x.000,00 
Item 4                              x.000,00 
Item 5                              000,00 
This is the conclusions for this list


Bookmark3

a)  Another example of text that needs to go in the mergefield:
•   Article 1 xxxx  Quantity/Producer etc
•   Article 2 xxxx  Quantity/Producer etc
Some details about this block of text that is not relevant but I need to insert it in the merge field as well

因此,如果按下某个单选按钮,则“Bookmark1”/“Bookmark2”/“Bookmark3”之后的整个文本需要进入其特定的合并字段。我已经为这些文本块添加了书签。正如我上面告诉你的那样,它只会插入一些没有子弹的行。例如,对应于Bookmark2的合并字段仅接收“这是多个项目的列表:”。

1 个答案:

答案 0 :(得分:1)

查看您的文档和代码,我发现有两个地方可能是您问题的根源:

首先:包含Bookmark1的SecondTemplate.docx的xml布局如下:

<Paragraph>
    <Bookmarkstart name=bookmark1/>
    <Run>
        <Text "Item 1">
    </Run>
</Paragraph>
<Paragraph>
    <Run>
        <Text "Item 2">
    </Run>
</Paragraph>    
<Paragraph>
    <Run>
        <Text "Item 3">
    </Run>
</Paragraph>    
<Paragraph>
    <Run>
        <Text "Item 4">
    </Run>
    <Bookmarkend/>
</Paragraph>    

以及您的代码:

            if(bookmarkStart.Name == bookmarkKey)
            {
                foreach(Run run in bookmarkStart.Parent.Descendants<Run>())
                {
                    returnVal += run.Descendants<Text>().FirstOrDefault().Text + "<br/>";
                }
            }

bookmarkstart.Parent调用运行时,它会匹配书签正上方的Paragraph

<Paragraph>
    <Bookmarkstart name=bookmark1/>
    <Run>
        <Text "Item 1">
    </Run>
</Paragraph>

因此当循环的其余部分执行时,您只会将“项目1”拉入合并过程。您需要重新设置逻辑,以便在BookmarkStart和BookmarkEnd之间的所有四个段落中正确匹配Run中的Text。

第二:在OpenXml中经常绊倒的另一个问题是,当您尝试匹配后代调用中的Run时:

 bookmarkStart.Parent.Descendants<Run>

如果你指的是DocumentFormat.OpenXml.Drawing.Run,而不是正确的'DocumentFormat.OpenXml.Wordprocessing.Run',这可以防止匹配 - 所以鼠标悬停在Visual Studio中的Run并确保你是匹配正确的运行。调整using语句以获得正确的语句。像

这样的使用语句
using Run = DocumentFormat.OpenXml.Wordprocessing.Run;
通常使用

,具体取决于该文件中的其余代码。希望这些线索可以帮助你。