这是使用 C# 从 ObservableCollection 创建 Word 文件的后续问题。
我有一个.docx文件,其中包含一个Body
,其中有 2 列用于其SectionProperties
.我有一本外语词典及其翻译。在每一行上,我需要 [Word] = [翻译],每当一个新字母开始时,它都应该在自己的行中,在该字母前后有 2 或 3 个换行符,如下所示:
一个
A字=翻译
A字=翻译
乙
B字=翻译
B字=翻译
...
我在 for 循环中构建了它,以便在每次迭代中我都会创建一个新段落,其中包含字母的可能Run
(如果开始一个新段落(、单词的Run
和翻译的Run
。因此,带有第一个字母的Run
与单词和翻译Run
处于同一Paragraph
,并且在Text
前后附加了 2 或 3 个Break
对象。
这样做时,第二列有时可以从 1 或 2 个空行开始。或者下一页上的第一列可以以空行开头。
这就是我想避免的。
所以我的问题是,我可以以某种方式检查是否到达页面末尾,或者文本是否位于列的顶部,这样我就不必添加Break
?或者,我可以格式化Column
本身,使其不以空行开头吗?
我尝试将字母Run
放在单独的可选Paragraph
中,但同样,我发现自己必须输入换行符,问题仍然存在。
本着我另一个答案的精神,您可以扩展模板功能。使用快捷会议工具生成单个分页符对象,如下所示:
private readonly Paragraph PageBreakPara = new Paragraph(new Run(new Break() { Type = BreakValues.Page}));
创建一个帮助程序方法来查找文本标记的容器:
public IEnumerable FindElements(OpenXmlCompositeElement searchParent, string tagRegex( 其中 T:OpenXmlElement{ var regex = new Regex(tagRegex(;
return searchParent.Descendants()
.Where(e=>(!(e is OpenXmlCompositeElement)
&& regex.IsMatch(e.InnerText)))
.SelectMany(e =>
e.Ancestors()
.OfType<T>()
.Union(e is T ? new T[] { (T)e } : new T[] {} ))
.ToList(); // can skip, prevents reevaluations
}
另一个从文档中复制范围并删除范围:
public IEnumerable<T> DuplicateRange<T>(OpenXmlCompositeElement root, string tagRegex)
where T: OpenXmlElement
{
// tagRegex must describe exactly two tags, such as [pageStart] and [pageEnd]
// or [page] [/page] - or whatever pattern you choose
var tagElements = FindElements(root, tagRegex);
var fromEl = tagElements.First();
var toEl = tagElements.Skip(1).First(); // throws exception if less than 2 el
// you may want to find a common parent here
// I'll assume you've prepared the template so the elements are siblings.
var result = new List<OpenXmlElement>();
var step = fromEl.NextSibling();
while (step !=null && toEl!=null && step!=toEl){
// another method called DeleteRange will instead delete elements in that range within this loop
var copy = step.CloneNode();
toEl.InsertAfterSelf(copy);
result.Add(copy);
step = step.NextSibling();
}
return result;
}
public IEnumerable<OpenXmlElement> ReplaceTag(OpenXmlCompositeElement parent, string tagRegex, string replacement){
var replaceElements = FindElements<OpenXmlElement>(parent, tagRegex);
var regex = new Regex(tagRegex);
foreach(var el in replaceElements){
el.InnerText = regex.Replace(el.InnerText, replacement);
}
return replaceElements;
}
现在,您可以拥有如下所示的文档:
[页][标题信]
[单词模板][单词]: [翻译] [/单词模板]
[分页][/页]
使用该文档,您可以复制[页面]。[/page] 范围,按字母处理,一旦没有字母 - 删除模板范围:
var 词汇 = 字典>;
foreach (var letter in vocabulary.Keys.OrderByDescending(c=>c)){
// in reverse order because the copy range comes after the template range
var pageTemplate = DuplicateRange(wordDocument,"\[/?page\]");
foreach (var p in pageTemplate.OfType<OpenXmlCompositeElement>()){
ReplaceTag(p, "[TitleLetter]",""+letter);
var pageBr = ReplaceTag(p, "[pageBreak]","");
if (pageBr.Any()){
foreach(var pbr in pageBr){
pbr.InsertAfterSelf(PageBreakPara.CloneNode());
}
}
var wordTemplateFound = FindElements(p, "\[/?WordTemplate\]");
if (wordTemplateFound .Any()){
foreach (var word in vocabulary[letter].Keys){
var wordTemplate = DuplicateRange(p, "\[/?WordTemplate\]")
.First(); // since it's a single paragraph template
ReplaceTag(wordTemplate, "\[/?WordTemplate\]","");
ReplaceTag(wordTemplate, "\[Word]",word);
ReplaceTag(wordTemplate, "\[Translation\]",vocabulary[letter][word]);
}
}
}
}
。或类似的东西。
- 如果事情开始变得过于复杂,请查看 SdtElements
- 不要使用 AltChunk,尽管这个答案很受欢迎,但它需要 Word 来打开和处理文件,所以你不能使用一些库来制作 PDF
- Word文档很乱,上面的解决方案应该可以工作(尚未测试(,但模板必须精心制作,经常备份模板
- 制作一个强大的文档引擎并不容易(因为 Word 很混乱(,做你需要的最低限度,并依赖于你控制的模板(不是用户可编辑的(。
- 上面的代码远未优化或简化,我试图以可呈现性为代价将其压缩在尽可能小的占用空间中。可能也有错误:)