义乌网站建设软件开发,招生网站建设方案,北京大企业公司排名,网页设计技术学什么将很早之前写的一个小组件重新整理优化一下#xff0c;做成一个通用的功能。适用于导出数据库的结构#xff08;表、字段等#xff09;到Word或将体检数据自动生成Word版的体检报告等。代码#xff1a;Github 一、主要需要完成功能: 1. 灵活的配置规则及word样式设置#… 将很早之前写的一个小组件重新整理优化一下做成一个通用的功能。适用于导出数据库的结构表、字段等到Word或将体检数据自动生成Word版的体检报告等。代码Github 一、主要需要完成功能: 1. 灵活的配置规则及word样式设置文本、表格、图表、颜色等. 2. 支持表格. 3. 支持图表. 4. 支持章节内容循环生成. 5. 支持目录. 6.支持文档结构图 7.更新指定位置的文字 8.支持pdf导出. 最后结果如下: 图一 图二 图三 二、需求分析与实现方式 功能主要涉及3个比较重要的部分数据源、Word样式、配置规则。 为了简单,数据源决定采用一个存储过程返回Dataset的方式, 整张报告的数据来源于此Dataset的多个Datatable. 样式与配置首先想到的是写一个config文件所有配置都放到一个文件里然后将数据按照这个规则生成word。但无疑这样的配置项太多了关键是“样式”问题比如字体、颜色、表格宽度.....想想就头大。而且没有“所见即所得”的效果配置完都不知道啥样。 后来决定采取修改的方式, 先以一个word文件作为模板在模板中定义好上面提到的“样式”然后在模板中做一个个标记然后将数据按照规则更新到对应的标记。 图四 而这个标记我们用到了word的一个叫【书签】的功能打开word按ctrlshiftF5, 打开书签功能如下图 图五 这样将【规则】通过一系列规则的【书签】定义到word模板中。 三、规则配置 思路确定了那就开始设计如何通过【书签】将规则定义到word模板中去这里决定将所有规则都通过【书签】实现而放弃config文件的方式这个更统一而且直观一些。 A.循环 以图四为例数据库有多少张表是不固定的我们在制作模板的时候不可能先画好NN为表的总数个表格等待数据填充 这里就会需要遍历数据源中提供的所有表结构数据然后逐一形成表格。这里就需要将图四中的表格循环一下自动复制生成多个这样的表格。当然这只是一种情况还有可能会出现循环嵌套循环的情况那么我将这个循环定义成一个书签的时候按照这样的格式 loop_级别_表序号_filter_名称 含义如下 loop代表这是一个循环。 级别默认文档级别为0出现的第一层循环为1其内部若再次嵌套循环则级别为2依次类推。 表序号取Dataset中的第几张表(从1开始) filter循环的时候可能会用到对datatable的查找过滤在此写出多个字段用XX隔开(因为此处不允许有下划线外其他特殊字符, 就用这个XX吧 ) 名称loop名称方便与其他 loop区别 B.更新指定位置的文字 如图四中的【服务器名】、【表总数】等只需要替换对应的文字即可 label_级别_名称 含义如下 label代表这是一个label。 级别默认文档级别为0出现的第一层循环为1其内部若再次嵌套循环则级别为2依次类推。 名称label名称 注意这里省略了表序号当级别为0的时候 自动取最后一个datatable中的数据因为这个label经常会用到其他表汇总的数据可能会用到之前几张表的数据所以放在其他表都处理好后。当级别为1的时候自然取该级别循环的行数据。 C.表格 表格的配置原本也想对表格添加书签,后来发现有个表格属性, 觉得写在这里更好一些。 如上图所示【标题】格式为table_级别_取Dataset中的第几张表(从1开始)_filter字段多个用XX隔开(此处不允许有下划线外其他特殊字符, 就用这个XX吧 )_名称 【说明】为可选项若需要合计行, 则需要标识, summary或缩写s: [合计]行是模板中表格的第几行 summaryfilter或缩写sf:数据集进一步filter到summary行的条件(因为一个表格只取一个Datatable,通过一个标识指定了哪些datarow是用来作为合计的) D.图表 同样为了方便将配置写在了【标题】图表生成后会将名称修改过来。 配置格式为:chart_级别_取Dataset中的第几张表(从1开始)_filter字段多个用XX隔开(此处不允许有下划线外其他特殊字符, 就用这个XX吧 )_chart名称_是否将Datatable的columnName作为第一行_从datatable第几列开始(列起始为1)_截止列 如下图所示配置即可。 E.目录 无需标识, 模板中添加目录, 当内容处理完成之后, 会根据处理后的结果动态更新目录. 四、主要代码 1 using System;2 using System.Collections.Generic;3 using System.Data;4 using System.Diagnostics;5 using System.IO;6 using System.Linq;7 using System.Reflection;8 using Excel Microsoft.Office.Interop.Excel;9 using Word Microsoft.Office.Interop.Word;10 11 namespace FlyLolo.WordReport.Demo12 {13 public class WordReportHelper14 {15 private Word.Application wordApp null;16 private Word.Document wordDoc null;17 private DataSet dataSource null;18 private object line Word.WdUnits.wdLine;19 private string errorMsg ;20 21 /// summary22 /// 根据模板文件,创建数据报告23 /// /summary24 /// param nametemplateFile模板文件名(含路径)/param25 /// param namenewFilePath新文件路径)/param26 /// param namedataSource数据源,包含多个datatable/param27 /// param namesaveFormat新文件格式:/param28 public bool CreateReport(string templateFile, DataSet dataSource, out string errorMsg, string newFilePath, ref string newFileName, int saveFormat 16)29 {30 this.dataSource dataSource;31 errorMsg this.errorMsg;32 bool rtn OpenTemplate(templateFile)33 SetContent(new WordElement(wordDoc.Range(), dataRow: dataSource.Tables[dataSource.Tables.Count - 1].Rows[0]))34 UpdateTablesOfContents()35 SaveFile(newFilePath, ref newFileName, saveFormat);36 37 CloseAndClear();38 return rtn;39 }40 41 /// summary42 /// 打开模板文件43 /// /summary44 /// param nametemplateFile/param45 /// returns/returns46 private bool OpenTemplate(string templateFile)47 {48 if (!File.Exists(templateFile))49 {50 return false;51 }52 53 wordApp new Word.Application();54 wordApp.Visible true;//使文档可见,调试用55 wordApp.DisplayAlerts Word.WdAlertLevel.wdAlertsNone;56 object file templateFile;57 wordDoc wordApp.Documents.Open(ref file, ReadOnly: false);58 return true;59 }60 61 /// summary62 /// 为指定区域写入数据63 /// /summary64 /// param nameelement/param65 /// returns/returns66 private bool SetContent(WordElement element)67 {68 string currBookMarkName string.Empty;69 string startWith loop_ (element.Level 1).ToString() _;70 foreach (Word.Bookmark item in element.Range.Bookmarks)71 {72 currBookMarkName item.Name;73 74 if (currBookMarkName.StartsWith(startWith) (!currBookMarkName.Equals(element.ElementName)))75 {76 SetLoop(new WordElement(item.Range, currBookMarkName, element.DataRow, element.GroupBy));77 }78 79 }80 81 SetLabel(element);82 83 SetTable(element);84 85 SetChart(element);86 87 return true;88 }89 90 /// summary91 /// 处理循环92 /// /summary93 /// param nameelement/param94 /// returns/returns95 private bool SetLoop(WordElement element)96 {97 DataRow[] dataRows dataSource.Tables[element.TableIndex].Select(element.GroupByString);98 int count dataRows.Count();99 element.Range.Select();
100
101 //第0行作为模板 先从1开始 循环后处理0行;
102 for (int i 0; i count; i)
103 {
104
105 element.Range.Copy(); //模板loop复制
106 wordApp.Selection.InsertParagraphAfter();//换行 不会清除选中的内容,TypeParagraph 等同于回车,若当前有选中内容会被清除. TypeParagraph 会跳到下一行,InsertParagraphAfter不会, 所以movedown一下.
107 wordApp.Selection.MoveDown(ref line, Missing.Value, Missing.Value);
108 wordApp.Selection.Paste(); //换行后粘贴复制内容
109 int offset wordApp.Selection.Range.End - element.Range.End; //计算偏移量
110
111 //复制书签,书签名 模板书签名 复制次数
112 foreach (Word.Bookmark subBook in element.Range.Bookmarks)
113 {
114 if (subBook.Name.Equals(element.ElementName))
115 {
116 continue;
117 }
118
119 wordApp.Selection.Bookmarks.Add(subBook.Name _ i.ToString(), wordDoc.Range(subBook.Start offset, subBook.End offset));
120 }
121
122 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));
123 }
124
125 element.Range.Delete();
126
127 return true;
128 }
129
130 /// summary
131 /// 处理简单Label
132 /// /summary
133 /// param nameelement/param
134 /// returns/returns
135 private bool SetLabel(WordElement element)
136 {
137 if (element.Range.Bookmarks ! null element.Range.Bookmarks.Count 0)
138 {
139 string startWith label_ element.Level.ToString() _;
140 string bookMarkName string.Empty;
141 foreach (Word.Bookmark item in element.Range.Bookmarks)
142 {
143 bookMarkName item.Name;
144
145 if (bookMarkName.StartsWith(startWith))
146 {
147 bookMarkName WordElement.GetName(bookMarkName);
148
149 item.Range.Text element.DataRow[bookMarkName].ToString();
150 }
151 }
152 }
153
154 return true;
155 }
156
157 /// summary
158 /// 填充Table
159 /// /summary
160 /// param nameelement/param
161 /// returns/returns
162 private bool SetTable(WordElement element)
163 {
164 if (element.Range.Tables ! null element.Range.Tables.Count 0)
165 {
166 string startWith table_ element.Level.ToString() _;
167 foreach (Word.Table table in element.Range.Tables)
168 {
169 if (!string.IsNullOrEmpty(table.Title) table.Title.StartsWith(startWith))
170 {
171 WordElement tableElement new WordElement(null, table.Title, element.DataRow);
172
173 TableConfig config new TableConfig(table.Descr);
174
175 object dataRowTemplate table.Rows[config.DataRow];
176 Word.Row SummaryRow null;
177 DataRow SummaryDataRow null;
178 DataTable dt dataSource.Tables[tableElement.TableIndex];
179 DataRow[] dataRows dataSource.Tables[tableElement.TableIndex].Select(tableElement.GroupByString); ;
180
181 if (config.SummaryRow 0)
182 {
183 SummaryRow table.Rows[config.SummaryRow];
184 SummaryDataRow dt.Select(string.IsNullOrEmpty(tableElement.GroupByString) ? config.SummaryFilter : tableElement.GroupByString and config.SummaryFilter).FirstOrDefault();
185 }
186
187 foreach (DataRow row in dataRows)
188 {
189 if (row SummaryDataRow)
190 {
191 continue;
192 }
193
194 Word.Row newRow table.Rows.Add(ref dataRowTemplate);
195 for (int j 0; j table.Columns.Count; j)
196 {
197 newRow.Cells[j 1].Range.Text row[j].ToString(); ;
198 }
199
200 }
201
202 ((Word.Row)dataRowTemplate).Delete();
203
204 if (config.SummaryRow 0 SummaryDataRow ! null)
205 {
206 for (int j 0; j SummaryRow.Cells.Count; j)
207 {
208 string temp SummaryRow.Cells[j 1].Range.Text.Trim().Replace(\r\a, );
209
210 if (!string.IsNullOrEmpty(temp) temp.Length 2 dt.Columns.Contains(temp.Substring(1, temp.Length - 2)))
211 {
212 SummaryRow.Cells[j 1].Range.Text SummaryDataRow[temp.Substring(1, temp.Length - 2)].ToString();
213 }
214 }
215 }
216
217 table.Title tableElement.Name;
218 }
219
220
221 }
222 }
223
224 return true;
225 }
226
227 /// summary
228 /// 处理图表
229 /// /summary
230 /// param nameelement/param
231 /// returns/returns
232 private bool SetChart(WordElement element)
233 {
234 if (element.Range.InlineShapes ! null element.Range.InlineShapes.Count 0)
235 {
236 ListWord.InlineShape chartList element.Range.InlineShapes.CastWord.InlineShape().Where(m m.Type Word.WdInlineShapeType.wdInlineShapeChart).ToList();
237 string startWith chart_ element.Level.ToString() _;
238 foreach (Word.InlineShape item in chartList)
239 {
240 Word.Chart chart item.Chart;
241 if (!string.IsNullOrEmpty(chart.ChartTitle.Text) chart.ChartTitle.Text.StartsWith(startWith))
242 {
243 WordElement chartElement new WordElement(null, chart.ChartTitle.Text, element.DataRow);
244
245 DataTable dataTable dataSource.Tables[chartElement.TableIndex];
246 DataRow[] dataRows dataTable.Select(chartElement.GroupByString);
247
248 int columnCount dataTable.Columns.Count;
249 Listint columns new Listint();
250
251 foreach (var dr in dataRows)
252 {
253 for (int i chartElement.ColumnStart -1 ? 0 : chartElement.ColumnStart - 1; i (chartElement.ColumnEnd -1 ? columnCount : chartElement.ColumnEnd); i)
254 {
255 if (columns.Contains(i) || dr[i] null || string.IsNullOrEmpty(dr[i].ToString()))
256 {
257
258 }
259 else
260 {
261 columns.Add(i);
262 }
263 }
264 }
265 columns.Sort();
266 columnCount columns.Count;
267 int rowsCount dataRows.Length;
268
269 Word.ChartData chartData chart.ChartData;
270
271 //chartData.Activate();
272 //此处有个比较疑惑的问题, 不执行此条,生成的报告中的图表无法再次右键编辑数据. 执行后可以, 但有两个问题就是第一会弹出Excel框, 处理完后会自动关闭. 第二部分chart的数据range设置总不对
273 //不知道是不是版本的问题, 谁解决了分享一下,谢谢
274
275 Excel.Workbook dataWorkbook (Excel.Workbook)chartData.Workbook;
276 dataWorkbook.Application.Visible false;
277
278 Excel.Worksheet dataSheet (Excel.Worksheet)dataWorkbook.Worksheets[1];
279 //设定范围
280 string a (chartElement.ColumnNameForHead ? rowsCount 1 : rowsCount) | columnCount;
281 Console.WriteLine(a);
282
283 Excel.Range tRange dataSheet.Range[A1, dataSheet.Cells[(chartElement.ColumnNameForHead ? rowsCount 1 : rowsCount), columnCount]];
284 Excel.ListObject tbl1 dataSheet.ListObjects[1];
285 //dataSheet.ListObjects[1].Delete(); //想过重新删除再添加 这样 原有数据清掉了, 但觉得性能应该会有所下降
286 //Excel.ListObject tbl1 dataSheet.ListObjects.AddEx();
287 tbl1.Resize(tRange);
288 for (int j 0; j rowsCount; j)
289 {
290 DataRow row dataRows[j];
291 for (int k 0; k columnCount; k)
292 {
293 dataSheet.Cells[j 2, k 1].FormulaR1C1 row[columns[k]];
294 }
295 }
296
297 if (chartElement.ColumnNameForHead)
298 {
299 for (int k 0; k columns.Count; k)
300 {
301 dataSheet.Cells[1, k 1].FormulaR1C1 dataTable.Columns[columns[k]].ColumnName;
302 }
303 }
304 chart.ChartTitle.Text chartElement.Name;
305 //dataSheet.Application.Quit();
306 }
307 }
308 }
309
310 return true;
311 }
312
313 /// summary
314 /// 更新目录
315 /// /summary
316 /// returns/returns
317 private bool UpdateTablesOfContents()
318 {
319 foreach (Word.TableOfContents item in wordDoc.TablesOfContents)
320 {
321 item.Update();
322 }
323
324 return true;
325 }
326
327 /// summary
328 /// 保存文件
329 /// /summary
330 /// param namenewFilePath/param
331 /// param namenewFileName/param
332 /// param namesaveFormat/param
333 /// returns/returns
334 private bool SaveFile(string newFilePath, ref string newFileName, int saveFormat 16)
335 {
336 if (string.IsNullOrEmpty(newFileName))
337 {
338 newFileName DateTime.Now.ToString(yyyyMMddHHmmss);
339
340 switch (saveFormat)
341 {
342 case 0:// Word.WdSaveFormat.wdFormatDocument
343 newFileName .doc;
344 break;
345 case 16:// Word.WdSaveFormat.wdFormatDocumentDefault
346 newFileName .docx;
347 break;
348 case 17:// Word.WdSaveFormat.wdFormatPDF
349 newFileName .pdf;
350 break;
351 default:
352 break;
353 }
354 }
355
356 object newfile Path.Combine(newFilePath, newFileName);
357 object wdSaveFormat saveFormat;
358 wordDoc.SaveAs(ref newfile, ref wdSaveFormat);
359 return true;
360 }
361
362 /// summary
363 /// 清理
364 /// /summary
365 private void CloseAndClear()
366 {
367 if (wordApp null)
368 {
369 return;
370 }
371 wordDoc.Close(Word.WdSaveOptions.wdDoNotSaveChanges);
372 wordApp.Quit(Word.WdSaveOptions.wdDoNotSaveChanges);
373 System.Runtime.InteropServices.Marshal.ReleaseComObject(wordDoc);
374 System.Runtime.InteropServices.Marshal.ReleaseComObject(wordApp);
375 wordDoc null;
376 wordApp null;
377 GC.Collect();
378 KillProcess(Excel, WINWORD);
379 }
380
381 /// summary
382 /// 杀进程..
383 /// /summary
384 /// param nameprocessNames/param
385 private void KillProcess(params string[] processNames)
386 {
387 //Process myproc new Process();
388 //得到所有打开的进程
389 try
390 {
391 foreach (string name in processNames)
392 {
393 foreach (Process thisproc in Process.GetProcessesByName(name))
394 {
395 if (!thisproc.CloseMainWindow())
396 {
397 if (thisproc ! null)
398 thisproc.Kill();
399 }
400 }
401 }
402 }
403 catch (Exception)
404 {
405 //throw Exc;
406 // msg.Text 杀死 processName 失败;
407 }
408 }
409 }
410
411 /// summary
412 /// 封装的Word元素
413 /// /summary
414 public class WordElement
415 {
416 public WordElement(Word.Range range, string elementName , DataRow dataRow null, Dictionarystring, string groupBy null, int tableIndex 0)
417 {
418 this.Range range;
419 this.ElementName elementName;
420 this.GroupBy groupBy;
421 this.DataRow dataRow;
422 if (string.IsNullOrEmpty(elementName))
423 {
424 this.Level 0;
425 this.TableIndex tableIndex;
426 this.Name string.Empty;
427 this.ColumnNameForHead false;
428 }
429 else
430 {
431 string[] element elementName.Split(_);
432 this.Level int.Parse(element[1]);
433 this.ColumnNameForHead false;
434 this.ColumnStart -1;
435 this.ColumnEnd -1;
436
437 if (element[0].Equals(label))
438 {
439 this.Name element[2];
440 this.TableIndex 0;
441 }
442 else
443 {
444 this.Name element[4];
445 this.TableIndex int.Parse(element[2]) - 1;
446
447 if (!string.IsNullOrEmpty(element[3]))
448 {
449 string[] filters element[3].Split(new string[] { XX }, StringSplitOptions.RemoveEmptyEntries);
450 if (this.GroupBy null)
451 {
452 this.GroupBy new Dictionarystring, string();
453 }
454 foreach (string item in filters)
455 {
456 if (!this.GroupBy.Keys.Contains(item))
457 {
458 this.GroupBy.Add(item, dataRow[item].ToString());
459 }
460
461 }
462 }
463
464 if (element[0].Equals(chart) element.Count() 5)
465 {
466 this.ColumnNameForHead element[5].Equals(1);
467 this.ColumnStart string.IsNullOrEmpty(element[6]) ? -1 : int.Parse(element[6]);
468 this.ColumnEnd string.IsNullOrEmpty(element[7]) ? -1 : int.Parse(element[7]);
469 }
470 }
471 }
472 }
473
474 public Word.Range Range { get; set; }
475 public int Level { get; set; }
476 public int TableIndex { get; set; }
477 public string ElementName { get; set; }
478
479 public DataRow DataRow { get; set; }
480 public Dictionarystring, string GroupBy { get; set; }
481
482 public string Name { get; set; }
483
484 public bool ColumnNameForHead { get; set; }
485 public int ColumnStart { get; set; }
486 public int ColumnEnd { get; set; }
487
488 public string GroupByString
489 {
490 get
491 {
492 if (GroupBy null || GroupBy.Count 0)
493 {
494 return string.Empty;
495 }
496
497 string rtn string.Empty;
498 foreach (string key in this.GroupBy.Keys)
499 {
500 rtn and key GroupBy[key] ;
501 }
502 return rtn.Substring(3);
503 }
504 }
505
506 public static string GetName(string elementName)
507 {
508 string[] element elementName.Split(_);
509
510
511 if (element[0].Equals(label))
512 {
513 return element[2];
514 }
515 else
516 {
517 return element[4];
518 }
519 }
520 }
521
522 /// summary
523 /// Table配置项
524 /// /summary
525 public class TableConfig
526 {
527 public TableConfig(string tableDescr )
528 {
529 this.DataRow 2;
530 this.SummaryRow -1;
531
532 if (!string.IsNullOrEmpty(tableDescr))
533 {
534 string[] element tableDescr.Split(,);
535 foreach (string item in element)
536 {
537 if (!string.IsNullOrEmpty(item))
538 {
539 string[] configs item.Split(:);
540 if (configs.Length 2)
541 {
542 switch (configs[0].ToLower())
543 {
544 case data:
545 case d:
546 this.DataRow int.Parse(configs[1]);
547 break;
548 case summary:
549 case s:
550 this.SummaryRow int.Parse(configs[1]);
551 break;
552 case summaryfilter:
553 case sf:
554 this.SummaryFilter configs[1];
555 break;
556 default:
557 break;
558 }
559 }
560 }
561 }
562 }
563
564 }
565 public int DataRow { get; set; }
566 public int SummaryRow { get; set; }
567 public string SummaryFilter { get; set; }
568 }
569 } View Code 转载于:https://www.cnblogs.com/FlyLolo/p/WordReport.html