C# 處理Word自動生成報告 四、程式處理

来源:http://www.cnblogs.com/FlyLolo/archive/2017/11/23/7884527.html
-Advertisement-
Play Games

現在說一下程式處理部分,有點長 本來是想做針對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, 希望能完美解決上面的問題.


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 本文針對Hi3518EV200平臺處理器,通過ADC單次採樣方式,實現對多通道(1~4通道)ADC進行採樣控制。本文僅僅是對Hi3518EV200晶元ADC的用法的介紹,不涉及ADC具體的工作原理、轉換原理等細節內容。 ...
  • DMA(Direct Memory Access) 即直接存儲器訪問, DMA 傳輸方式無需 CPU 直接控制傳輸,通過硬體為 RAM 、I/O 設備開闢一條直接傳送數據的通路,能使 CPU 的效率大為提高。 學了這麼多驅動,不難推出DMA的編寫套路: 1)註冊DMA中斷,分配緩衝區 2)註冊字元設 ...
  • man 幫助文檔 使用方法:man 命令名,比如man man;簡單查看方法:命令名 --help 或者 help 命令名 ls 羅列文件夾內容 tree 查看目錄結構 cd 切換目錄 pwd 查看當前目錄 cp 複製 mv 移動;文件重命名 find 查找 eg find / -name "文件名 ...
  • SUSE12自定義安裝跟以往版本差不多,只是調整了一些功能安裝順序,例如網路設置放到很靠前,SUSE11的時候幾乎是在後半部分,自定義分區也調整到網路設置之後,入口設置也隱秘,如果是熟悉suse11安裝,很容易略過這一部分。 --趙偉江 目錄 啟動安裝模式 選擇安裝語言 網路設置 激活郵件以及序列號 ...
  • 安裝Centos7後,系統自動更新狀態預設為開啟,若禁止系統自動更新需要手動關閉。 1.進入yum目錄 [root@localhost ~]$ cd /etc/yum 2.編輯yum-cron.conf文件 [root@localhost yum]$ vi yum-cron.conf 3.把down ...
  • 寫這篇沒有參考任何文章,只是寫下自己一路使用的體會 linux與windows給我的感覺有點像flask之於django.linux只有一個小而強大的內核,各種應用軟體配置自由,就連桌面也有不同的可供選擇。 linux的歷史就不介紹了,百度都有。說一下各個發行版: 發行版之間的區別說白了就是軟體的搭 ...
  • 上面的代碼是我們在使用繼承時,子類和父類的構造函數的寫法。構造方法能否被繼承,我覺得應該這樣理解: 父類是對子類的抽象,也就是說父類和子類可以合併寫成一個類,合併後的類寫出來的構造函數,不僅會包含子類的構造函數的內容,還會包含父類的構造函數的內容,所以我覺得這裡不能看成是子類繼承父類的構造函數,看成 ...
  • Q:i want to pass my List as parameter using my eventpublic event EventHandler _newFileEventHandler; List _filesList = new List();public void startList... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...