現在說一下程式處理部分,有點長 本來是想做針對doc和docx的模板兩個版本, 後來想到可以在生成的時候saveas里設置格式, 所以此版只支持對docx的模板處理, 想要doc的情況可以選擇生成格式為doc的. 上代碼: public class WordHelper { private 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> public bool CreateReport(string templateFile, DataSet dataSource, out string errorMsg, string newFilePath, ref string newFileName, int saveFormat = 16) { this.dataSource = dataSource; errorMsg = this.errorMsg; bool rtn = OpenTemplate(templateFile) && SetContent(new WordElement(wordDoc.Range(), dataRow: dataSource.Tables[dataSource.Tables.Count - 1].Rows[0])) && 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; string startWith = "loop_" + (element.Level + 1).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行; for (int i = 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) { if (element.Range.Bookmarks != null && element.Range.Bookmarks.Count > 0) { 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) { if (element.Range.Tables != null && element.Range.Tables.Count > 0) { 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); ; if (config.SummaryRow > 0) { 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); for (int j = 0; j < table.Columns.Count; j++) { newRow.Cells[j + 1].Range.Text = row[j].ToString(); ; } } ((Word.Row)dataRowTemplate).Delete(); if (config.SummaryRow > 0 && SummaryDataRow != null) { for (int j = 0; j < SummaryRow.Cells.Count; j++) { string temp = SummaryRow.Cells[j + 1].Range.Text.Trim().Replace("\r\a", ""); if (!string.IsNullOrEmpty(temp) && temp.Length > 2 && dt.Columns.Contains(temp.Substring(1, temp.Length - 2))) { SummaryRow.Cells[j + 1].Range.Text = SummaryDataRow[temp.Substring(1, temp.Length - 2)].ToString(); } } } table.Title = tableElement.Name; } } } return true; } private bool SetChart(WordElement element) { if (element.Range.InlineShapes != null && element.Range.InlineShapes.Count > 0) { 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) { for (int i = chartElement.ColumnStart == -1 ? 0 : chartElement.ColumnStart - 1; i < (chartElement.ColumnEnd == -1 ? 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[1]; //設定範圍 string a = (chartElement.ColumnNameForHead ? rowsCount + 1 : rowsCount) + "|" + columnCount; Console.WriteLine(a); Excel.Range tRange = dataSheet.Range["A1", dataSheet.Cells[(chartElement.ColumnNameForHead ? rowsCount + 1 : rowsCount), columnCount]]; Excel.ListObject tbl1 = dataSheet.ListObjects[1]; //dataSheet.ListObjects[1].Delete(); //想過重新刪除再添加 這樣 原有數據清掉了, 但覺得性能應該會有所下降 //Excel.ListObject tbl1 = dataSheet.ListObjects.AddEx(); tbl1.Resize(tRange); for (int j = 0; j < rowsCount; j++) { DataRow row = dataRows[j]; for (int k = 0; k < columnCount; k++) { dataSheet.Cells[j + 2, k + 1].FormulaR1C1 = row[columns[k]]; } } if (chartElement.ColumnNameForHead) { for (int k = 0; k < columns.Count; k++) { dataSheet.Cells[1, k + 1].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; } private bool SaveFile(string newFilePath, ref string newFileName, int saveFormat = 16) { if (string.IsNullOrEmpty(newFileName)) { newFileName = DateTime.Now.ToString("yyyyMMddHHmmss"); switch (saveFormat) { case 0:// Word.WdSaveFormat.wdFormatDocument newFileName += ".doc"; break; case 16:// Word.WdSaveFormat.wdFormatDocumentDefault newFileName += ".docx"; break; case 17:// 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 { public WordElement(Word.Range range, string elementName = "", DataRow dataRow = null, Dictionary<string, string> groupBy = null, int tableIndex = 0) { this.Range = range; this.ElementName = elementName; this.GroupBy = groupBy; this.DataRow = dataRow; if (string.IsNullOrEmpty(elementName)) { this.Level = 0; this.TableIndex = tableIndex; this.Name = string.Empty; this.ColumnNameForHead = false; } else { string[] element = elementName.Split('_'); this.Level = int.Parse(element[1]); this.ColumnNameForHead = false; this.ColumnStart = -1; this.ColumnEnd = -1; if (element[0].Equals("label")) { this.Name = element[2]; this.TableIndex = 0; } else { this.Name = element[4]; this.TableIndex = int.Parse(element[2]) - 1; if (!string.IsNullOrEmpty(element[3])) { string[] filters = element[3].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()); } } } if (element[0].Equals("chart") && element.Count() > 5) { this.ColumnNameForHead = element[5].Equals("1"); this.ColumnStart = string.IsNullOrEmpty(element[6]) ? -1 : int.Parse(element[6]); this.ColumnEnd = string.IsNullOrEmpty(element[7]) ? -1 : int.Parse(element[7]); } } } } 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 { if (GroupBy == null || GroupBy.Count == 0) { return string.Empty; } string rtn = string.Empty; foreach (string key in this.GroupBy.Keys) { rtn += "and " + key + " = '" + GroupBy[key] + "' "; } return rtn.Substring(3); } } public static string GetName(string elementName) { string[] element = elementName.Split('_'); if (element[0].Equals("label")) { return element[2]; } else { return element[4]; } } } public class TableConfig { public TableConfig(string tableDescr = "") { this.DataRow = 2; this.SummaryRow = -1; if (!string.IsNullOrEmpty(tableDescr)) { string[] element = tableDescr.Split(','); foreach (string item in element) { if (!string.IsNullOrEmpty(item)) { string[] configs = item.Split(':'); if (configs.Length == 2) { switch (configs[0].ToLower()) { case "data": case "d": this.DataRow = int.Parse(configs[1]); break; case "summary": case "s": this.SummaryRow = int.Parse(configs[1]); break; case "summaryfilter": case "sf": this.SummaryFilter = configs[1]; break; default: break; } } } } } } public int DataRow { get; set; } public int SummaryRow { get; set; } public string SummaryFilter { get; set; } }View Code
後續問題: 1.部署: 目前我的開發環境里裝的是office2016 引用的是 Microsoft Word 16.0 Object Library, 對應的Microsoft.Office.Interop.Word.dll版本是15.0...
發現office2013版本也是15, 只是小版本不同, 程式部署過去執行失敗, 還沒研究是什麼原因. 另外沒找到office 2016的 primary interop assembly, 莫非部署的環境里也要安裝完整的office?
2. word中嵌入的Excel圖表的問題, 雖然生成結果中的圖表數據是正確的, 但無法右鍵再次編輯. 不知道和我的版本有沒有關係.
3. 性能問題: 處理速度較慢.
希望有知道的看到給個回覆. 打算有時間研究一下OpenXML, 希望能完美解決上面的問題.