TL; DR我可以使用.NET生成Word文档,例如XAML ItemTemplates吗?
我发现很难找到满足我所有要求的解决方案,所以我想我会把它扔到stackoverflow,希望有人可以指导我。非常感谢提前。
简单地说,我需要根据数据库中的数据生成Word文档。
我理想的解决方案:我希望它能像DataTemplates在xaml中的工作方式一样工作。我发现了大量的示例和解决方案,其中模板表示静态文档,需要替换(单个)动态内容。
e.g。 WordDocGenerator
问题是,我需要一个解决方案,我可以为层次结构的每个级别指定模板,文档生成器将使用这些项级别模板基于文档级模板构建最终文档。
我的具体要求是:
假设数据层次结构是这样的
class Country
{
public string Name { get; set; }
public IList<City> { get; set; }
}
class City
{
public string Name { get; set; }
public IList<Suburb> { get; set;}
}
class Suburb
{
public string Name { get; set; }
public int Postcode { get; set; }
}
在我看来,解决方案将是一个函数调用,它接受一个国家/地区列表。
// returns path to generated document
public static string GenerateDocument(IList<Country> countries);
此功能将接受国家/地区列表以及每个国家/地区
最后,这些生成的文档“片段”将使用文档级别模板累积到一个最终的Word文档中,该模板将指定“标题”页面,页眉/页脚,TOC。
答案 0 :(得分:3)
答案 1 :(得分:1)
我最终得到了我想要的东西。在Eric White的文章的帮助下,我手动完成了所有工作。
所以对这个来源的品味如此。有一个模板,确保前三个段落是您想要的3个层次结构。循环遍历集合,克隆节点,替换文本,重复。
private const string COUNTRY_TITLE = "[[CountryTitle]]"
private const string CITY_TITLE = "[[CityTitle]]"
private const string SUBURB_TITLE = "[[SuburbTitle]]"
using (WordprocessingDocument myDoc = WordprocessingDocument.Open(outputPath, true))
{
var mainPart = myDoc.MainDocumentPart;
var body = mainPart.Document.Body;
var originalCountryPara = body.ElementAt(0);
var originalCityPara = body.ElementAt(1);
var originalSuburbPara = body.ElementAt(2);
foreach (var country in Countries)
{
if (!String.IsNullOrEmpty(country.Title))
{
// clone Country level node on template
var clonedCountry = originalCountryPara.CloneNode(true);
// replace Country title
Helpers.CompletelyReplaceText(clonedCountry as Paragraph, COUNTRY_TITLE, country.Title);
body.AppendChild(clonedCountry);
}
foreach (var city in country.Cities)
{
if (!String.IsNullOrEmpty(city.Title))
{
// clone City level node on template
var clonedCity = originalCityPara.CloneNode(true);
// replace City title
Helpers.CompletelyReplaceText(clonedCity as Paragraph, CITY_TITLE, city.Title);
body.AppendChild(clonedCity);
}
foreach (var suburb in city.Suburbs)
{
// clone Suburb level node on template
var clonedSuburb = originalSuburbPara.CloneNode(true);
// replace Suburb title
Helpers.CompletelyReplaceText(clonedSuburb as Paragraph, SUBURB_TITLE, suburb.Title);
body.AppendChild(clonedSuburb);
}
}
}
body.RemoveChild(originalCountryPara);
body.RemoveChild(originalCityPara);
body.RemoveChild(originalSuburbPara);
mainPart.Document.Save();
}
/// <summary>
/// 'Completely' refers to the fact this method replaces the whole paragraph with newText if it finds
/// existingText in this paragraph. The only thing spared is the pPr (paragraph properties)
/// </summary>
/// <param name="paragraph"></param>
/// <param name="existingText"></param>
/// <param name="newText"></param>
public static void CompletelyReplaceText(Paragraph paragraph, string existingText, string newText)
{
StringBuilder stringBuilder = new StringBuilder();
foreach (var text in paragraph.Elements<Run>().SelectMany(run => run.Elements<Text>()))
{
stringBuilder.Append(text.Text);
}
string paraText = stringBuilder.ToString();
if (!paraText.Contains(existingText)) return;
// remove everything here except properties node
foreach (var element in paragraph.Elements().ToList().Where(element => !(element is ParagraphProperties)))
{
paragraph.RemoveChild(element);
}
// insert new run with text
var newRun = new Run();
var newTextNode = new Text(newText);
newRun.AppendChild(newTextNode);
paragraph.AppendChild(newRun);
}