我正在开发一个解决方案,将产生字的文件。 字文档已定义的内容控件模板文件的基础上产生的。 一切都为我工作很好,当我只有一个在我的模板内容控制,但更多的内容控制扩展模板文档后,我得到异常。 好像我没有找到的内容控制。
这是我的方法:
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.InvalidOperationException”类型的异常出现在System.Core.dll但在用户代码中没有处理。 的InnerException:空
我如何能写出寻找更好的控制方法,为我任何提示?
你的问题是,您的来电中的一个(或多个) Single()
被调用在具有多个元素的序列。 该文档的Single()
国家(重点煤矿):
返回序列中的唯一元素,如果有序列中的一个元素是不是抛出异常。
在你的代码这可以在两种情况之一发生。 第一个是,如果你有相同的多个控制Tag
的值,例如,你可能有一个标有“姓”的文档中两个控制这将意味着这条线
var lastName = mainPart.Document.Body.Descendants<SdtBlock>().Where
(r => r.SdtProperties.GetFirstChild<Tag>().Val == "LastName")
将返回两个元素。
第二个是,如果你的内容的控制有一个以上的Text
在这种情况下,该行元素在里面
var t = firstName.Descendants<Text>();
将返回多个元素。 例如,如果我创建与内容“这是一个测试”我结束了XML具有4个的控制Text
元素:
<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
内容,或只是一个特定的一个(如第一个或最后一个)。
如果您要更换只有一个你可以改变从呼叫Single()
以First()
或Last()
的例子,但我猜你需要全部更换。 在这种情况下,你需要周围的每一个匹配的元素环路要更换每个标签的名字。
卸下调用Single()
将返回IEnumerable<SdtBlock>
您可以遍历更换左右各一:
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;
}
要解决的第二个问题是稍微棘手。 在我看来, 最简单的解决方案是从内容删除所有现有的段落,然后添加一个新的使用要输出的文本。
打破这一点成为可能的方法是有道理的,因为是有大量重复的代码 - 沿着这些线路应该这样做的东西:
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);
}
}
然后可以从你的代码中调用像这样:
using (var wordDoc = WordprocessingDocument.Open(mem, true))
{
var mainPart = wordDoc.MainDocumentPart;
ReplaceTags(mainPart, "FirstName", _firstName);
ReplaceTags(mainPart, "LastName", _lastName);
mainPart.Document.Save();
SaveFileToSp(mem);
}
我觉得你的Single()
方法导致异常。
当你有只有一个内容控制, Single()
可以得到唯一可用的元素。 但是,当你展开内容的控制,你的Single()
方法会导致InvalidOperationException
,因为序列中的一个以上的元素是。 如果是这样的情况下,尽量循环代码,并采取一个元素在同一时间。