使用Open XML SDK检索内容控件时出现问题 [英] Problems retrieving content controls with Open XML sdk

查看:337
本文介绍了使用Open XML SDK检索内容控件时出现问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一种将生成Word文档的解决方案.单词文档是根据具有定义的内容控件的模板文档生成的.当我的模板中只有一个内容控件时,一切对我都很好,但是在使用更多的内容控件扩展模板文档后,我遇到了异常.似乎我找不到内容控件.

I am developing a solution that will generate word-documents. The word-documents are generated on the basis of a template document which has defined content controls. Everything was working good for me when I had only one content control in my template, but after expanding the template document with more content controls, I am getting exceptions. It seems like I am not finding the content controls.

这是我的方法:

private void CreateReport(File file)

    {
        var byteArray = file.OpenBinary();
        using (var mem = new MemoryStream())
        {
            mem.Write(byteArray, 0, byteArray.Length);
            try
            {
                using (var wordDoc = WordprocessingDocument.Open(mem, true))
                {
                    var mainPart = wordDoc.MainDocumentPart;

                    var firstName = mainPart.Document.Body.Descendants<SdtBlock>().Where
                        (r => r.SdtProperties.GetFirstChild<Tag>().Val == "FirstName").Single();
                    var t = firstName.Descendants<Text>().Single();
                    t.Text = _firstName;

                     var lastName = mainPart.Document.Body.Descendants<SdtBlock>().Where
                        (r => r.SdtProperties.GetFirstChild<Tag>().Val == "LastName").Single();
                     var t2= lastName.Descendants<Text>().Single();
                     t2.Text = _lastName;

                    mainPart.Document.Save();
                    SaveFileToSp(mem);
                }

            }
            catch (FileFormatException)
            {
            }
        }
    }

这是我得到的例外:

System.Core.dll中发生类型'System.InvalidOperationException'的异常,但未在用户代码中处理.内部异常:空

An exception of type 'System.InvalidOperationException' occurred in System.Core.dll but was not handled in user code. Innerexception: Null

关于我如何编写更好的控件查找方法的任何提示?

Any tips for me on how I can write better method for finding controls?

推荐答案

您的问题是,在具有多个元素的序列上调用了Single()的一个(或多个)调用. 文档 >状态(重点是我的):

Your issue is that one (or more) of your calls to Single() is being called on a sequence that has more than one element. The documentation for Single() states (emphasis mine):

返回序列中的唯一元素,如果序列中没有一个元素,则抛出异常.

在您的代码中,这可能在两种情况之一中发生.第一种是如果您有多个具有相同Tag值的控件,例如,您在文档中可能会用标有"LastName"的两个控件来表示这一行

In your code this can happen in one of two scenarios. The first is if you have more than one control with the same Tag value, for example you might have two controls in the document labelled "LastName" which would mean that this line

var lastName = mainPart.Document.Body.Descendants<SdtBlock>().Where
                    (r => r.SdtProperties.GetFirstChild<Tag>().Val == "LastName")

将返回两个元素.

第二个是您的内容控件中是否包含多个Text元素,在这种情况下,此行

The second is if your content control has more than one Text element in it in which case this line

var t = firstName.Descendants<Text>();

将返回多个元素.例如,如果我创建一个内容为"This is is a test"的控件,那么我最终得到的XML包含4个Text元素:

would return multiple elements. For example if I create a control with the content "This is a test" I end up with XML which has 4 Text elements:

<w:p w:rsidR="00086A5B" w:rsidRDefault="003515CB">
    <w:r>
        <w:rPr>
            <w:rStyle w:val="PlaceholderText" />
        </w:rPr>
        <w:t xml:space="preserve">This </w:t>
    </w:r>
    <w:r>
        <w:rPr>
            <w:rStyle w:val="PlaceholderText" />
            <w:i />
        </w:rPr>
        <w:t>is</w:t>
    </w:r>
    <w:r>
        <w:rPr>
            <w:rStyle w:val="PlaceholderText" />
        </w:rPr>
        <w:t xml:space="preserve"> </w:t>
    </w:r>
    <w:r w:rsidR="00E1178E">
        <w:rPr>
            <w:rStyle w:val="PlaceholderText" />
        </w:rPr>
        <w:t>a test</w:t>
    </w:r>
</w:p>

如何解决第一个问题取决于您是希望替换匹配的Tag元素的 all 还是只替换一个特定的元素(例如第一个或最后一个).

How to get round the first issue depends on whether you wish to replace all of the matching Tag elements or just one particular one (such as the first or last).

如果只想替换一个,可以将呼叫从Single()更改为First()Last(),但是我想您需要全部替换.在这种情况下,您需要为每个要替换的标签名称在每个匹配的元素周围循环.

If you want to replace just one you can change the call from Single() to First() or Last() for example but I guess you need to replace them all. In that case you need to loop around each matching element for each tag name you wish to replace.

将呼叫移至Single()将返回一个IEnumerable<SdtBlock>,您可以反复进行替换:

Removing the call to Single() will return an IEnumerable<SdtBlock> which you can iterate around replacing each one:

IEnumerable<SdtBlock> firstNameFields = mainPart.Document.Body.Descendants<SdtBlock>().Where
    (r => r.SdtProperties.GetFirstChild<Tag>().Val == "FirstName");

foreach (var firstName in firstNameFields)
{
    var t = firstName.Descendants<Text>().Single();
    t.Text = _firstName;
}

解决第二个问题要棘手一些.我认为最简单的解决方案是从内容中删除所有现有的段落,然后添加一个新的段落,其中包含您要输出的文本.

To get around the second problem is slightly more tricky. The easiest solution in my opinion is to remove all of the existing paragraphs from the content and then add a new one with the text you wish to output.

将其分解为一种方法可能是有意义的,因为有很多重复的代码-遵循以下几行原则应该可以做到这一点:

Breaking this out into a method probably makes sense as there's a lot of repeated code - something along these lines should do it:

private static void ReplaceTags(MainDocumentPart mainPart, string tagName, string tagValue)
{
    //grab all the tag fields
    IEnumerable<SdtBlock> tagFields = mainPart.Document.Body.Descendants<SdtBlock>().Where
        (r => r.SdtProperties.GetFirstChild<Tag>().Val == tagName);

    foreach (var field in tagFields)
    {
        //remove all paragraphs from the content block
        field.SdtContentBlock.RemoveAllChildren<Paragraph>();
        //create a new paragraph containing a run and a text element
        Paragraph newParagraph = new Paragraph();
        Run newRun = new Run();
        Text newText = new Text(tagValue);
        newRun.Append(newText);
        newParagraph.Append(newRun);
        //add the new paragraph to the content block
        field.SdtContentBlock.Append(newParagraph);
    }
}

然后可以从您的代码中调用以下代码:

Which can then be called from your code like so:

using (var wordDoc = WordprocessingDocument.Open(mem, true))
{
    var mainPart = wordDoc.MainDocumentPart;

    ReplaceTags(mainPart, "FirstName", _firstName);
    ReplaceTags(mainPart, "LastName", _lastName);

    mainPart.Document.Save();
    SaveFileToSp(mem);
}

这篇关于使用Open XML SDK检索内容控件时出现问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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