C# 处理Word自动生成报告 四、程序处理
现在说一下程序处理部分,有点长
本来是想做针对doc和docx的模板两个版本, 后来想到可以在生成的时候saveas里设置格式,
所以此版只支持对docx的模板处理,
想要doc的情况可以选择生成格式为doc的.
上代码:
public class WordHelper { private Word.Application wordApp = null; private Word.Document wordDoc = null; private DataSet dataSource = null; private object line = Word.WdUnits.wdLine; private string errorMsg = ""; /// <summary> /// 根据模板文件,创建数据报告 /// </summary> /// <param name="templateFile">模板文件名(含路径)</param> /// <param name="newFilePath">新文件路径)</param> /// <param name="dataSource">数据源,包含多个datatable</param> /// <param name="saveFormat">新文件格式:</param> ) { this.dataSource = dataSource; errorMsg = this.errorMsg; bool rtn = OpenTemplate(templateFile) && SetContent(].Rows[])) && UpdateTablesOfContents() && SaveFile(newFilePath, ref newFileName, saveFormat); CloseAndClear(); return rtn; } private bool OpenTemplate(string templateFile) { if (!File.Exists(templateFile)) { return false; } wordApp = new Word.ApplicationClass(); wordApp.Visible = false;//使文档可见,调试用 wordApp.DisplayAlerts = Word.WdAlertLevel.wdAlertsNone; object file = templateFile; wordDoc = wordApp.Documents.Open(ref file, ReadOnly: true); return true; } private bool SetContent(WordElement element) { string currBookMarkName = string.Empty; ).ToString() + "_"; foreach (Word.Bookmark item in element.Range.Bookmarks) { currBookMarkName = item.Name; if (currBookMarkName.StartsWith(startWith) && (!currBookMarkName.Equals(element.ElementName))) { SetLoop(new WordElement(item.Range, currBookMarkName, element.DataRow, element.GroupBy)); } } SetLabel(element); SetTable(element); SetChart(element); return true; } private bool SetLoop(WordElement element) { DataRow[] dataRows = dataSource.Tables[element.TableIndex].Select(element.GroupByString); int count = dataRows.Count(); element.Range.Select(); //第0行作为模板 先从1开始 循环后处理0行; ; i < count; i++) { element.Range.Copy(); //模板loop复制 wordApp.Selection.InsertParagraphAfter();//换行 不会清除选中的内容,TypeParagraph 等同于回车,若当前有选中内容会被清除. TypeParagraph 会跳到下一行,InsertParagraphAfter不会, 所以movedown一下. wordApp.Selection.MoveDown(ref line, Missing.Value, Missing.Value); wordApp.Selection.Paste(); //换行后粘贴复制内容 int offset = wordApp.Selection.Range.End - element.Range.End; //计算偏移量 //复制书签,书签名 = 模板书签名 + 复制次数 foreach (Word.Bookmark subBook in element.Range.Bookmarks) { if (subBook.Name.Equals(element.ElementName)) { continue; } wordApp.Selection.Bookmarks.Add(subBook.Name + "_" + i.ToString(), wordDoc.Range(subBook.Start + offset, subBook.End + offset)); } SetContent(new WordElement(wordDoc.Range(wordApp.Selection.Range.End - (element.Range.End - element.Range.Start), wordApp.Selection.Range.End), element.ElementName + "_" + i.ToString(), dataRows[i], element.GroupBy)); } element.Range.Delete(); return true; } private bool SetLabel(WordElement element) { ) { string startWith = "label_" + element.Level.ToString() + "_"; string bookMarkName = string.Empty; foreach (Word.Bookmark item in element.Range.Bookmarks) { bookMarkName = item.Name; if (bookMarkName.StartsWith(startWith)) { bookMarkName = WordElement.GetName(bookMarkName); item.Range.Text = element.DataRow[bookMarkName].ToString(); } } } return true; } private bool SetTable(WordElement element) { ) { string startWith = "table_" + element.Level.ToString() + "_"; foreach (Word.Table table in element.Range.Tables) { if (!string.IsNullOrEmpty(table.Title) && table.Title.StartsWith(startWith)) { WordElement tableElement = new WordElement(null, table.Title, element.DataRow); TableConfig config = new TableConfig(table.Descr); object dataRowTemplate = table.Rows[config.DataRow]; Word.Row SummaryRow = null; DataRow SummaryDataRow = null; DataTable dt = dataSource.Tables[tableElement.TableIndex]; DataRow[] dataRows = dataSource.Tables[tableElement.TableIndex].Select(tableElement.GroupByString); ; ) { SummaryRow = table.Rows[config.SummaryRow]; SummaryDataRow = dt.Select(string.IsNullOrEmpty(tableElement.GroupByString) ? config.SummaryFilter : tableElement.GroupByString + " and " + config.SummaryFilter).FirstOrDefault(); } foreach (DataRow row in dataRows) { if (row == SummaryDataRow) { continue; } Word.Row newRow = table.Rows.Add(ref dataRowTemplate); ; j < table.Columns.Count; j++) { newRow.Cells[j + ].Range.Text = row[j].ToString(); ; } } ((Word.Row)dataRowTemplate).Delete(); && SummaryDataRow != null) { ; j < SummaryRow.Cells.Count; j++) { ].Range.Text.Trim().Replace("\r\a", ""); && dt.Columns.Contains(temp.Substring(, temp.Length - ))) { SummaryRow.Cells[j + ].Range.Text = SummaryDataRow[temp.Substring(, temp.Length - )].ToString(); } } } table.Title = tableElement.Name; } } } return true; } private bool SetChart(WordElement element) { ) { List<Word.InlineShape> chartList = element.Range.InlineShapes.Cast<Word.InlineShape>().Where(m => m.Type == Word.WdInlineShapeType.wdInlineShapeChart).ToList(); string startWith = "chart_" + element.Level.ToString() + "_"; foreach (Word.InlineShape item in chartList) { Word.Chart chart = item.Chart; if (!string.IsNullOrEmpty(chart.ChartTitle.Text) && chart.ChartTitle.Text.StartsWith(startWith)) { WordElement chartElement = new WordElement(null, chart.ChartTitle.Text, element.DataRow); DataTable dataTable = dataSource.Tables[chartElement.TableIndex]; DataRow[] dataRows = dataTable.Select(chartElement.GroupByString); int columnCount = dataTable.Columns.Count; List<int> columns = new List<int>(); foreach (var dr in dataRows) { ? : chartElement.ColumnStart - ; i < (chartElement.ColumnEnd == - ? columnCount : chartElement.ColumnEnd); i++) { if (columns.Contains(i) || dr[i] == null || string.IsNullOrEmpty(dr[i].ToString())) { } else { columns.Add(i); } } } columns.Sort(); columnCount = columns.Count; int rowsCount = dataRows.Length; Word.ChartData chartData = chart.ChartData; //chartData.Activate(); //此处有个比较疑惑的问题, 不执行此条,生成的报告中的图表无法再次右键编辑数据. 执行后可以, 但有两个问题就是第一会弹出Excel框, 处理完后会自动关闭. 第二部分chart的数据range设置总不对 //不知道是不是版本的问题, 谁解决了分享一下,谢谢 Excel.Workbook dataWorkbook = (Excel.Workbook)chartData.Workbook; dataWorkbook.Application.Visible = false; Excel.Worksheet dataSheet = (Excel.Worksheet)dataWorkbook.Worksheets[]; //设定范围 : rowsCount) + "|" + columnCount; Console.WriteLine(a); Excel.Range tRange = dataSheet.Range[ : rowsCount), columnCount]]; Excel.ListObject tbl1 = dataSheet.ListObjects[]; //dataSheet.ListObjects[1].Delete(); //想过重新删除再添加 这样 原有数据清掉了, 但觉得性能应该会有所下降 //Excel.ListObject tbl1 = dataSheet.ListObjects.AddEx(); tbl1.Resize(tRange); ; j < rowsCount; j++) { DataRow row = dataRows[j]; ; k < columnCount; k++) { dataSheet.Cells[j + , k + ].FormulaR1C1 = row[columns[k]]; } } if (chartElement.ColumnNameForHead) { ; k < columns.Count; k++) { dataSheet.Cells[, k + ].FormulaR1C1 = dataTable.Columns[columns[k]].ColumnName; } } chart.ChartTitle.Text = chartElement.Name; //dataSheet.Application.Quit(); } } } return true; } private bool UpdateTablesOfContents() { foreach (Word.TableOfContents item in wordDoc.TablesOfContents) { item.Update(); } return true; } ) { if (string.IsNullOrEmpty(newFileName)) { newFileName = DateTime.Now.ToString("yyyyMMddHHmmss"); switch (saveFormat) { :// Word.WdSaveFormat.wdFormatDocument newFileName += ".doc"; break; :// Word.WdSaveFormat.wdFormatDocumentDefault newFileName += ".docx"; break; :// Word.WdSaveFormat.wdFormatPDF newFileName += ".pdf"; break; default: break; } } object newfile = Path.Combine(newFilePath, newFileName); object wdSaveFormat = saveFormat; wordDoc.SaveAs(ref newfile, ref wdSaveFormat); return true; } private void CloseAndClear() { if (wordApp == null) { return; } wordDoc.Close(Word.WdSaveOptions.wdDoNotSaveChanges); wordApp.Quit(Word.WdSaveOptions.wdDoNotSaveChanges); System.Runtime.InteropServices.Marshal.ReleaseComObject(wordDoc); System.Runtime.InteropServices.Marshal.ReleaseComObject(wordApp); wordDoc = null; wordApp = null; GC.Collect(); KillProcess("Excel","WINWORD"); } private void KillProcess(params string[] processNames) { //Process myproc = new Process(); //得到所有打开的进程 try { foreach (string name in processNames) { foreach (Process thisproc in Process.GetProcessesByName(name)) { if (!thisproc.CloseMainWindow()) { if (thisproc != null) thisproc.Kill(); } } } } catch (Exception) { //throw Exc; // msg.Text+= "杀死" + processName + "失败!"; } } } public class WordElement { ) { this.Range = range; this.ElementName = elementName; this.GroupBy = groupBy; this.DataRow = dataRow; if (string.IsNullOrEmpty(elementName)) { ; this.TableIndex = tableIndex; this.Name = string.Empty; this.ColumnNameForHead = false; } else { string[] element = elementName.Split('_'); ]); this.ColumnNameForHead = false; ; ; ].Equals("label")) { ]; ; } else { ]; ]) - ; ])) { ].Split(new string[] { "XX" }, StringSplitOptions.RemoveEmptyEntries); if (this.GroupBy == null) { this.GroupBy = new Dictionary<string, string>(); } foreach (string item in filters) { if (!this.GroupBy.Keys.Contains(item)) { this.GroupBy.Add(item, dataRow[item].ToString()); } } } ].Equals() { ].Equals("); ]) ? - : ]); ]) ? - : ]); } } } } public Word.Range Range { get; set; } public int Level { get; set; } public int TableIndex { get; set; } public string ElementName { get; set; } public DataRow DataRow { get; set; } public Dictionary<string, string> GroupBy { get; set; } public string Name { get; set; } public bool ColumnNameForHead { get; set; } public int ColumnStart { get; set; } public int ColumnEnd { get; set; } public string GroupByString { get { ) { return string.Empty; } string rtn = string.Empty; foreach (string key in this.GroupBy.Keys) { rtn += "and " + key + " = '" + GroupBy[key] + "' "; } ); } } public static string GetName(string elementName) { string[] element = elementName.Split('_'); ].Equals("label")) { ]; } else { ]; } } } public class TableConfig { public TableConfig(string tableDescr = "") { ; ; if (!string.IsNullOrEmpty(tableDescr)) { string[] element = tableDescr.Split(','); foreach (string item in element) { if (!string.IsNullOrEmpty(item)) { string[] configs = item.Split(':'); ) { ].ToLower()) { case "data": case "d": ]); break; case "summary": case "s": ]); break; case "summaryfilter": case "sf": ]; break; default: break; } } } } } } public int DataRow { get; set; } public int SummaryRow { get; set; } public string SummaryFilter { get; set; } }
后续问题: 1.部署: 目前我的开发环境里装的是office2016 的office365版 引用的是 Microsoft Word 16.0 Object Library, 对应的Microsoft.Office.Interop.Word.dll版本是15.0...
发现office2013版本也是15, 只是小版本不同, 没找到office 2016 和2013的 primary interop assembly, 莫非部署的环境里也要安装完整的office?
为了避免文件占用问题,打开模板采用了只读方式打开wordDoc = wordApp.Documents.Open(ref file, ReadOnly: true); 在office2016(office365)版测试通过, 但专业版测试失败.
ReadOnly: false的情况下, office2016 office2013均测试通过.
2. word中嵌入的Excel图表的问题, 虽然生成结果中的图表数据是正确的, 但无法右键再次编辑数据, 只可修改样式.
3. 性能问题: 处理速度较慢.
希望有知道的看到给个回复. 打算有时间研究一下OpenXML, 希望能完美解决上面的问题.
C# 处理Word自动生成报告 四、程序处理的更多相关文章
- C# 处理Word自动生成报告 三、设计模板
C# 处理Word自动生成报告 一.概述 C# 处理Word自动生成报告 二.数据源例子 C# 处理Word自动生成报告 三.设计模板 C# 处理Word自动生成报告 四.程序处理 既然是模板就少不了 ...
- C# 处理Word自动生成报告 一、概述
经常遇到这样的需求, 生成Word格式的报告, 而不是单纯的一张表格的报表. 就像体检报告一样. 数据来源部分决定采用一个存储过程返回Dataset的方式, 整张报告的数据来源于此Dataset的多 ...
- C# 处理Word自动生成报告 二、数据源例子
还是以学生.语文.数学.分数为例吧, 感觉这个和helloworld都有一拼了. 造一张表如下, 整张报表就围绕这个表转圈了, 顺便说下就是名字如有雷同纯属巧合 新建个存储过程 ALTER PROCE ...
- Word自动生成目录
博主最近在写报告的时候要在Word里面做个目录,再做个页码,然后上网搜了一些方法,非常零散,我弄了好久才弄好.在这里我把整套方法分享一下. 声明:内容完全独创! 工具:Word 2016. 效果:如下 ...
- jmeter自动生成报告
从JMeter 3.0开始已支持自动生成动态报告,我们可以更容易根据生成的报告来完成我们的性能测试报告. 如何生成html测试报告 如果未生成结果文件(.jtl),可运行如下命令生成报告: jmete ...
- 利用html实现类似于word自动生成的目录的效果
在word中的自动生成目录当中,我们会看到是这样的目录结构: 嗯,自动生成固然是简单,但是在html当中,却没有一个合适的标签来去做.今天后台导出PDF的时候告诉我,他需要用html做一个这样的结构, ...
- 自动生成百度小程序sitemap.txt文件路径
因为业务需要,需要在目前项目上开发一个百度小程序,百度智能小程序上线了,但是内容每天得推送,不可能一个小程序路径一个推送吧,因为小程序路径和项目路径不一致. 因为项目是用ThinkPHP开发的,在此附 ...
- 使用LaTeX和KnitR自动生成报告
扩展名为.Rnw(Rtex)的文件就是包含了R代码的LaTeX文档.编译的时候,先用Rscript调用Knitr处理,生成.TeX文档,然后用pdfLaTeX/XeLaTeX编译成PDF. 最方便的编 ...
- 写了个自动生成vcxproj的程序
背景: 公司的vcxproj有个模板,必须要遵守 程序测试 config = { { ProjName = 'my_exe', ClCompile = {'main.cpp', 'main2.cpp' ...
随机推荐
- [Spark性能调优] 第三章 : Spark 2.1.0 中 Sort-Based Shuffle 产生的内幕
本課主題 Sorted-Based Shuffle 的诞生和介绍 Shuffle 中六大令人费解的问题 Sorted-Based Shuffle 的排序和源码鉴赏 Shuffle 在运行时的内存管理 ...
- centos7下固定IP(静态IP)网络配置
关于centos下网络配置(这里使用静态IP方法) 动态ip网络配置可参考我的另一篇博文http://www.cnblogs.com/albertrui/p/7811868.html 1.编辑/etc ...
- 一个InnoDB性能超过Oracle的调优Case
年前抽空到兄弟公司支援了一下Oracle迁移MySQL的测试,本想把MySQL调优到接近Oracle的性能即可,但经过 @何_登成 @淘宝丁奇 @淘宝褚霸 @淘伯松 诸位大牛的指导和帮助(排名不分先后 ...
- .net 平台下, Socket通讯协议中间件设计思路(附源码)
.net 平台下,实现通讯处理有很多方法(见下表),各有利弊: 序号 实现方式 特点 1 WCF 优点:封装好,方便.缺点:难学,不跨平台 2 RocketMQ,SuperSocket等中间件 优点: ...
- StackExchange.Redis学习笔记(五) 发布和订阅
Redis命令中的Pub/Sub Redis在 2.0之后的版本中 实现了 事件推送的 发布订阅命令 以下是Redis关于发布和订阅提供的相关命令 SUBSCRIBE channel [channe ...
- C#要点补充
1字符串与时间的互转 DateTime.TryParse将空字符串.为null或格式不正确,则转换为的DateTime所代表的值为:0001/1/1 0:00:00,此为DateTime.MinVal ...
- Django----->一周后的重温
Django 一 什么是web框架? 框架,即framework,特指为解决一个开放性问题而设计的具有一定约束性的支撑结构,使用框架可以帮你快速开发特定的系统,简单地说,就是你用别人搭建好的舞台来 ...
- 读书笔记《CSS权威指南》
阅读本书主要目的: 自从学会CSS以来,虽然熟练掌握了其使用方法和技巧,但对其底层的原理和实现并不清晰,阅读本书想进一步系统化的学习和深入研究其本质,对这门前端基础语言从熟练使用到真正理解. 第1章 ...
- IO(文件)处理
一.文件操作 1)介绍: 计算机系统分为:计算机硬件,操作系统,应用程序三部分. 我们用python或其他语言编写的应用程序若想要把数据永久保存下来,必须要保存于硬盘中,这就涉及到应用程序要操作硬件, ...
- sublime 设置新建文件自动添加author(作者)等文件头信息
很多时候, sublime 自带自动添加文件头信息, 但是并不是我们想要比如下面这样的:新建一个python文件 自动添加的author 信息== 上面并不是我想要的, 我想要下面这样的效果:== 这 ...