C#實現(xiàn)數(shù)據(jù)導出任一Word圖表的通用呈現(xiàn)方法
疲憊的修改
應人才測評產(chǎn)品的需求,導出測評報告是其中一個重要的環(huán)節(jié),報告的文件類型也多種多樣,其中WORD輸出也扮演了一個重要的角色。
實現(xiàn)方法比較簡單,結合分析結果數(shù)據(jù),通過WORD模板文件進行替換輸出。在實現(xiàn)的過程中,圖表的設計是必不可少的,根據(jù)初次產(chǎn)品的設計方案,圖表采用微軟Chart圖表控件進行開發(fā),采用雷達圖進行呈現(xiàn)。使用該控件首先要引入 System.Web.DataVisualization.dll 程序集,通過定義 System.Web.UI.DataVisualization.Charting.Chart 類來實現(xiàn),本來采用該開發(fā)方案的初衷是覺得都是微軟的技術,圖表的呈現(xiàn)類型也比較豐富,可在實際的開發(fā)中,情況沒有想像的那么順利,提供的技術文檔非常有限,各種百度也是鳳毛麟角,經(jīng)過努力與探索,最終還是實現(xiàn)了需求。
但后來由于種種原因,圖表要求采用餅狀3D圖進行呈現(xiàn),雖然已經(jīng)有了第一次的經(jīng)驗,但細節(jié)的變化,不得不再次進行探索和學習,可當需求再次改變的時候,我決定游說產(chǎn)品設計和改變設計思路。
新的思路
由于引入 Microsoft.Office.Interop.Word 程序集進行開發(fā),因此在Word上的所有操作都能用程序去實現(xiàn),其內(nèi)置的圖表功能也不例外,通過演練和內(nèi)部討論,圖形化的呈現(xiàn)基本能夠滿足需求。
通用性
舉例,我們在Word中插入一個圖表并選擇雷達圖,如下圖:
插入后,我們看到 Word 會自動彈出一個微縮版的 Excel 應用,改變其中的項和系列值,圖表就會對應的產(chǎn)生變化。
我們右擊雷達圖,選擇更改圖表類型為餅圖,如下圖:
可以看到餅圖按照EXCEL數(shù)據(jù)中的系列1數(shù)據(jù)進行呈現(xiàn),也不會因為系列2的數(shù)據(jù)存在而出現(xiàn)錯誤。由此可以分析出,控制好這個 Excel 的數(shù)據(jù)應用即可按照我們的設計實現(xiàn)任一圖表的輸出。
設計方案
(1)負責具體業(yè)務的應用程序,輸出后的數(shù)據(jù),存入一個二維字符串數(shù)組里,模擬 Excel 數(shù)據(jù)存儲模式。
(2)考慮未來的擴展性,將二維數(shù)組轉化為Json數(shù)據(jù)格式,并添加一個查找關鍵字節(jié)點,假設為“ t:chart1”。
(3)在 Word 模板設計圖表,圖表的標題設置為Json對應的查找關鍵字,即“ t:chart1”。
(4)編寫數(shù)據(jù)導出EXCEL方法,傳遞JSON字符串參數(shù),讀取Word模板文件,遍歷模板文件中的圖表對象,并按查找關鍵字與圖表的標題進行對比,匹配成功,則將JSON中數(shù)組轉化為圖表需要的EXCEL數(shù)組形式,到此輸出完畢。
為什么用 Json 過渡
我們的云架構里設計了一個 Office 計算中心,在某些環(huán)境下,比如 Linux 中需要這種方式傳遞并返回值,以達到導入導出Office文件的目的。所以大家要根據(jù)實際的應用進行設計,這里僅作為參考。
關鍵代碼實現(xiàn)
開發(fā)環(huán)境
操作系統(tǒng):Windows Server 2019 DataCenter
開發(fā)工具:VisualStudio2019
框架及語言:.net 4.7.1 C#
服務上需要安裝 Office 2016或以上
現(xiàn)在開始!
在此我們以最易懂的代碼形式舉例,假設文件模板中的圖表為條狀圖,關鍵查找字(圖表標題)設為 “ t:chart1”,如下圖:
(1)創(chuàng)建二維數(shù)組
//定義二維字符串數(shù)組,第一列為項目名稱,第二列為值 string[,] chart1 = new string[11, 2]; chart1[0, 0] = "項"; chart1[0, 1] = "值"; chart1[1, 0] = "全局觀"; chart1[2, 0] = "影響力"; chart1[3, 0] = "公正性"; chart1[4, 0] = "果敢性"; chart1[5, 0] = "執(zhí)行力"; chart1[6, 0] = "人際理解"; chart1[7, 0] = "成就意識"; chart1[8, 0] = "創(chuàng)新意識"; chart1[9, 0] = "情緒控制"; chart1[10, 0] = "學習發(fā)展"; Random rnd = new Random(); for (int si = 1; si <= 10; si++) { chart1[si, 0] = rnd.NextDouble().ToString(); //循環(huán)賦值隨機浮點數(shù) }
(2)二維數(shù)組轉Json格式
這里引入 Newtonsoft.Json.dll 程序集進行操作,代碼如下:
StringWriter sw = new StringWriter(); //using System.IO using (Newtonsoft.Json.JsonWriter writer = new Newtonsoft.Json.JsonTextWriter(sw)) { writer.Formatting = Newtonsoft.Json.Formatting.Indented; writer.WriteStartObject(); //t:chart1 轉化數(shù)組chart1 為 json 對象 writer.WritePropertyName("t:chart1"); writer.WriteStartArray(); writer.WriteStartObject(); writer.WritePropertyName("col1"); writer.WriteValue(chart1[0, 0]); writer.WritePropertyName("col2"); writer.WriteValue(chart1[0, 1]); writer.WriteEndObject(); for (int r = chart1.GetLength(0) - 1; r > 0; r--) { writer.WriteStartObject(); //循環(huán)寫入列2的具體值 for (int c = 0; c < 2; c++) { writer.WritePropertyName("col" + (c + 1).ToString()); writer.WriteValue(chart1[r, c]); } writer.WriteEndObject(); } writer.WriteEndArray(); //t:chart1 writer.WriteEndObject(); writer.Flush(); } sw.Close(); string jsonContent = sw.GetStringBuilder().ToString(); //得到最終json字串
轉化成功的樣例如下:
{ "t:chart1": [ { "col1": "項", "col2": "值" }, { "col1": "學習發(fā)展", "col2": "4.1" }, { "col1": "情緒控制", "col2": "5" }, { "col1": "創(chuàng)新意識", "col2": "5.1" }, { "col1": "成就意識", "col2": "4.8" }, { "col1": "人際理解", "col2": "4" }, { "col1": "執(zhí)行力", "col2": "5" }, { "col1": "果敢性", "col2": "5.7" }, { "col1": "公正性", "col2": "4.5" }, { "col1": "影響力", "col2": "4.7" }, { "col1": "全局觀", "col2": "4.2" } ] }
(3)查找圖表且替換數(shù)據(jù)
本代碼程序只是示例片斷,非完整程序,僅供參考。
一些引用 using Word=Microsoft.Office.Interop.Word; using Newtonsoft.Json.Linq; 轉換 json 字符串為 json 對象 Newtonsoft.Json.Linq.JObject jObject = null; if (jsonContent != "") { try { jObject = Newtonsoft.Json.Linq.JObject.Parse(jsonContent); //轉換為json對象 } catch (Exception e) { resultReport += "create json object fail.<br>"; //失敗記入調(diào)試報告 } } 初始化 Word 應用程序 Word.Application WordApp=new Word.Application(); //創(chuàng)建一個名為WordDoc的文檔對象 WordApp.DisplayAlerts=Word.WdAlertLevel.wdAlertsNone; //禁止一切提示警告 //打開 filename 的文件 Word.Document WordDoc=WordApp.Documents.Open(ref filename,ref Nothing,ref Nothing,ref Nothing,ref Nothing,ref Nothing,ref Nothing,ref Nothing,ref Nothing,ref Nothing,ref Nothing,ref Nothing,ref Nothing,ref Nothing,ref Nothing,ref Nothing); //禁用拼寫檢查 WordDoc.SpellingChecked = false; WordDoc.ShowSpellingErrors = false; 遍歷word 里的 shapes for (int i = 1; i <= WordDoc.InlineShapes.Count; i++) { Word.InlineShape shape = WordDoc.InlineShapes[i]; //得到 shape對象 //遍歷 json 對象 foreach (var item in jObject) { string tcmd = item.Key.ToString(); //取關鍵字 //如果 shape 包含圖表,則繼續(xù) if (shape.HasChart == Microsoft.Office.Core.MsoTriState.msoTrue) { //如果圖表未設置標題,則短路 if (shape.Chart.HasTitle == false) { continue; } //獲取圖表的title string _name = shape.Chart.ChartTitle.Text.Trim().ToLower(); if (_name.IndexOf(tcmd) != -1) //如果包含關鍵字則繼續(xù) { //替換掉關鍵字,保留下來的是真正的標題 shape.Chart.ChartTitle.Text = shape.Chart.ChartTitle.Text.Replace(tcmd, ""); //這是一個玄機,否則會報錯,目前我是這樣的解決,A1:Z100,先賦值為空串 shape.Chart.ChartData.Workbook.Worksheets[1].Range("A1:Z100").Value = ""; //計算最后的單元格地址 string lastcellAddress = "$" + ((char)(64 + jObject[tcmd][0].Count())).ToString() + "$" + jObject[tcmd].Count().ToString(); //獲得最終地址字串 string sourceDataAddress = "='Sheet1'!$A$1:" + lastcellAddress; //遍歷json對象節(jié)點里的數(shù)組 for (int i = 0; i < jObject[tcmd].Count(); i++) { List<JToken> tokens = jObject[tcmd][i].ToList(); int k = 0; foreach (JToken jToken in tokens) { //為每一個單元格賦值 string celladdress = ((char)(65 + k)).ToString() + (i + 1).ToString(); shape.Chart.ChartData.Workbook.Worksheets[1].Range(celladdress).Value = jToken.ToArray()[0].ToString(); k++; } } shape.Chart.SetSourceData(sourceDataAddress); //設置更新圖表的數(shù)據(jù)源 break; } // index of name } // has chart }//foreach tcmd } //WordDoc.InlineShapes
小結
通過這種設計可以實現(xiàn)任意更換圖表的類型,基本無需關注圖表的實現(xiàn)原理,而讓開發(fā)人員更多的關注于業(yè)務邏輯,當然這些圖表的種類受限于Word的提供能力,如果能夠滿足需求,不失為一種解決思路。另外,我們可以繼續(xù)擴展程序的功能,實現(xiàn)動態(tài)的圖表添加或切換能力等。
一些體會
作為一名全程管理加全棧開發(fā)的 “野戰(zhàn)軍”,更多的時候考慮的是滿足需求、穩(wěn)定功能和控制各種成本,而無法深入地研究各項領域。隨著年齡的增長,唯一能做到的就是業(yè)務經(jīng)驗彌補精力和學習時間的不足,還是有幾點體會與大家共勉吧:
1、后悔學生時代沒有端正態(tài)度和認識到認真學習的重要性,所謂書到用時方恨少,熟練掌握修煉數(shù)據(jù)結構與算法、數(shù)學等程序員的內(nèi)功至關重要。
2、語言只是一種模型和工具而已,可先從一門語言入手到實際應用,抽象的來看所有語言總體上都是大同小異,后來會覺得學習一門新語言是一件非常有趣的事。
3、時間允許的情況下還是要深入掌握一些底層的技術開發(fā)和原理,這至少是一件非常有趣的事。
4、在工作中平衡最為關鍵,也包括人,換位思考很重要。
5、提升設計能力、業(yè)務處理能力和總結學習方法尤為關鍵。
以上就是C#實現(xiàn)數(shù)據(jù)導出任一Word圖表的通用呈現(xiàn)方法的詳細內(nèi)容,更多關于C#數(shù)據(jù)導出Word圖表的資料請關注腳本之家其它相關文章!
相關文章
C#實現(xiàn)把txt文本數(shù)據(jù)快速讀取到excel中
這篇文章主要介紹了C#實現(xiàn)把txt文本數(shù)據(jù)快速讀取到excel中,本文直接給出示例代碼,需要的朋友可以參考下2015-06-06C#中的并發(fā)編程與.NET任務并行庫的使用示例和常見問題
在現(xiàn)代軟件開發(fā)中,.NET Framework通過引入任務并行庫(TPL)和并發(fā)集合類型,簡化了并發(fā)復雜性,提高程序的性能、可維護性和可擴展性,并發(fā)集合設計上允許多線程安全訪問,此外,TPL通過Task類簡化異步操作,正確使用這些工具可避免死鎖和競爭條件等常見問題2024-09-09