C#实现数据导出任一Word图表的通用呈现方法及一些体会

本文主要是介绍C#实现数据导出任一Word图表的通用呈现方法及一些体会,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

疲惫的修改

应人才测评产品的需求,导出测评报告是其中一个重要的环节,报告的文件类型也多种多样,其中WORD输出也扮演了一个重要的角色。

实现方法比较简单,结合分析结果数据,通过WORD模板文件进行替换输出。在实现的过程中,图表的设计是必不可少的,根据初次产品的设计方案,图表采用微软Chart图表控件进行开发,采用雷达图进行呈现。使用该控件首先要引入 System.Web.DataVisualization.dll 程序集,通过定义 System.Web.UI.DataVisualization.Charting.Chart 类来实现,本来采用该开发方案的初衷是觉得都是微软的技术,图表的呈现类型也比较丰富,可在实际的开发中,情况没有想像的那么顺利,提供的技术文档非常有限,各种百度也是凤毛麟角,经过努力与探索,最终还是实现了需求。

但后来由于种种原因,图表要求采用饼状3D图进行呈现,虽然已经有了第一次的经验,但细节的变化,不得不再次进行探索和学习,可当需求再次改变的时候,我决定游说产品设计和改变设计思路。

新的思路

由于引入 Microsoft.Office.Interop.Word 程序集进行开发,因此在Word上的所有操作都能用程序去实现,其内置的图表功能也不例外,通过演练和内部讨论,图形化的呈现基本能够满足需求。

通用性

举例,我们在Word中插入一个图表并选择雷达图,如下图:

插入后,我们看到 Word 会自动弹出一个微缩版的 Excel 应用,改变其中的项和系列值,图表就会对应的产生变化。  

我们右击雷达图,选择更改图表类型为饼图,如下图:

可以看到饼图按照EXCEL数据中的系列1数据进行呈现,也不会因为系列2的数据存在而出现错误。由此可以分析出,控制好这个 Excel 的数据应用即可按照我们的设计实现任一图表的输出。

设计方案

(1)负责具体业务的应用程序,输出后的数据,存入一个二维字符串数组里,模拟 Excel 数据存储模式。

(2)考虑未来的扩展性,将二维数组转化为Json数据格式,并添加一个查找关键字节点,假设为“ t:chart1”。

(3)在 Word 模板设计图表,图表的标题设置为Json对应的查找关键字,即“ t:chart1”。

(4)编写数据导出EXCEL方法,传递JSON字符串参数,读取Word模板文件,遍历模板文件中的图表对象,并按查找关键字与图表的标题进行对比,匹配成功,则将JSON中数组转化为图表需要的EXCEL数组形式,到此输出完毕。

为什么用 Json 过渡

我们的云架构里设计了一个 Office 计算中心,在某些环境下,比如 Linux 中需要这种方式传递并返回值,以达到导入导出Office文件的目的。所以大家要根据实际的应用进行设计,这里仅作为参考。

关键代码实现

开发环境

操作系统:Windows Server 2019 DataCenter

开发工具:VisualStudio2019 

框架及语言:.net 4.7.1     C#

服务上需要安装 Office 2016或以上

现在开始!

在此我们以最易懂的代码形式举例,假设文件模板中的图表为条状图,关键查找字(图表标题)设为 “ t:chart1”,如下图:

(1)创建二维数组

            //定义二维字符串数组,第一列为项目名称,第二列为值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] = "执行力";chart1[6, 0] = "人际理解";chart1[7, 0] = "成就意识";chart1[8, 0] = "创新意识";chart1[9, 0] = "情绪控制";chart1[10, 0] = "学习发展";Random rnd = new Random();for (int si = 1; si <= 10; si++){chart1[si, 0] = rnd.NextDouble().ToString(); //循环赋值随机浮点数}

(2)二维数组转Json格式

这里引入 Newtonsoft.Json.dll 程序集进行操作,代码如下:

    StringWriter sw = new StringWriter();   //using System.IOusing (Newtonsoft.Json.JsonWriter writer = new Newtonsoft.Json.JsonTextWriter(sw)){writer.Formatting = Newtonsoft.Json.Formatting.Indented;writer.WriteStartObject();//t:chart1  转化数组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();//循环写入列2的具体值for (int c = 0; c < 2; c++){writer.WritePropertyName("col" + (c + 1).ToString());writer.WriteValue(chart1[r, c]);}writer.WriteEndObject();}writer.WriteEndArray();//t:chart1writer.WriteEndObject();writer.Flush();}sw.Close();string jsonContent = sw.GetStringBuilder().ToString();  //得到最终json字串

转化成功的样例如下:

{
"t:chart1": [{"col1": "项","col2": "值"},{"col1": "学习发展","col2": "4.1"},{"col1": "情绪控制","col2": "5"},{"col1": "创新意识","col2": "5.1"},{"col1": "成就意识","col2": "4.8"},{"col1": "人际理解","col2": "4"},{"col1": "执行力","col2": "5"},{"col1": "果敢性","col2": "5.7"},{"col1": "公正性","col2": "4.5"},{"col1": "影响力","col2": "4.7"},{"col1": "全局观","col2": "4.2"}]
}

 

(3)查找图表且替换数据

本代码程序只是示例片断,非完整程序,仅供参考。

一些引用
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>";  //失败记入调试报告}}初始化 Word 应用程序Word.Application WordApp=new Word.Application();//创建一个名为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 里的 shapesfor (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 包含图表,则继续if (shape.HasChart == Microsoft.Office.Core.MsoTriState.msoTrue){//如果图表未设置标题,则短路if (shape.Chart.HasTitle == false){continue;}//获取图表的titlestring _name = shape.Chart.ChartTitle.Text.Trim().ToLower();if (_name.IndexOf(tcmd) != -1) //如果包含关键字则继续{//替换掉关键字,保留下来的是真正的标题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对象节点里的数组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);  //设置更新图表的数据源break;} // index of name} // has chart}//foreach tcmd} //WordDoc.InlineShapes

 小结

通过这种设计可以实现任意更换图表的类型,基本无需关注图表的实现原理,而让开发人员更多的关注于业务逻辑,当然这些图表的种类受限于Word的提供能力,如果能够满足需求,不失为一种解决思路。另外,我们可以继续扩展程序的功能,实现动态的图表添加或切换能力等。

一些体会

作为一名全程管理加全栈开发的 “野战军”,更多的时候考虑的是满足需求、稳定功能和控制各种成本,而无法深入地研究各项领域。随着年龄的增长,唯一能做到的就是业务经验弥补精力和学习时间的不足,还是有几点体会与大家共勉吧:

1、后悔学生时代没有端正态度和认识到认真学习的重要性,所谓书到用时方恨少,熟练掌握修炼数据结构与算法、数学等程序员的内功至关重要。

2、语言只是一种模型和工具而已,可先从一门语言入手到实际应用,抽象的来看所有语言总体上都是大同小异,后来会觉得学习一门新语言是一件非常有趣的事。

3、时间允许的情况下还是要深入掌握一些底层的技术开发和原理,这至少是一件非常有趣的事。

4、在工作中平衡最为关键,也包括人,换位思考很重要。

5、提升设计能力、业务处理能力和总结学习方法尤为关键。

以上就是自己一些体会,时间仓促,不妥之处还请大家批评指正讨论,程序员节就要到啦,祝咱们永远保持年轻的心,健康的心态,用智慧编写美好的人生!

这篇关于C#实现数据导出任一Word图表的通用呈现方法及一些体会的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/271550

相关文章

Python使用Tenacity一行代码实现自动重试详解

《Python使用Tenacity一行代码实现自动重试详解》tenacity是一个专为Python设计的通用重试库,它的核心理念就是用简单、清晰的方式,为任何可能失败的操作添加重试能力,下面我们就来看... 目录一切始于一个简单的 API 调用Tenacity 入门:一行代码实现优雅重试精细控制:让重试按我

Python安装Pandas库的两种方法

《Python安装Pandas库的两种方法》本文介绍了三种安装PythonPandas库的方法,通过cmd命令行安装并解决版本冲突,手动下载whl文件安装,更换国内镜像源加速下载,最后建议用pipli... 目录方法一:cmd命令行执行pip install pandas方法二:找到pandas下载库,然后

Redis客户端连接机制的实现方案

《Redis客户端连接机制的实现方案》本文主要介绍了Redis客户端连接机制的实现方案,包括事件驱动模型、非阻塞I/O处理、连接池应用及配置优化,具有一定的参考价值,感兴趣的可以了解一下... 目录1. Redis连接模型概述2. 连接建立过程详解2.1 连php接初始化流程2.2 关键配置参数3. 最大连

SpringBoot多环境配置数据读取方式

《SpringBoot多环境配置数据读取方式》SpringBoot通过环境隔离机制,支持properties/yaml/yml多格式配置,结合@Value、Environment和@Configura... 目录一、多环境配置的核心思路二、3种配置文件格式详解2.1 properties格式(传统格式)1.

Python实现网格交易策略的过程

《Python实现网格交易策略的过程》本文讲解Python网格交易策略,利用ccxt获取加密货币数据及backtrader回测,通过设定网格节点,低买高卖获利,适合震荡行情,下面跟我一起看看我们的第一... 网格交易是一种经典的量化交易策略,其核心思想是在价格上下预设多个“网格”,当价格触发特定网格时执行买

解决pandas无法读取csv文件数据的问题

《解决pandas无法读取csv文件数据的问题》本文讲述作者用Pandas读取CSV文件时因参数设置不当导致数据错位,通过调整delimiter和on_bad_lines参数最终解决问题,并强调正确参... 目录一、前言二、问题复现1. 问题2. 通过 on_bad_lines=‘warn’ 跳过异常数据3

python设置环境变量路径实现过程

《python设置环境变量路径实现过程》本文介绍设置Python路径的多种方法:临时设置(Windows用`set`,Linux/macOS用`export`)、永久设置(系统属性或shell配置文件... 目录设置python路径的方法临时设置环境变量(适用于当前会话)永久设置环境变量(Windows系统

Python对接支付宝支付之使用AliPay实现的详细操作指南

《Python对接支付宝支付之使用AliPay实现的详细操作指南》支付宝没有提供PythonSDK,但是强大的github就有提供python-alipay-sdk,封装里很多复杂操作,使用这个我们就... 目录一、引言二、准备工作2.1 支付宝开放平台入驻与应用创建2.2 密钥生成与配置2.3 安装ali

Spring Security 单点登录与自动登录机制的实现原理

《SpringSecurity单点登录与自动登录机制的实现原理》本文探讨SpringSecurity实现单点登录(SSO)与自动登录机制,涵盖JWT跨系统认证、RememberMe持久化Token... 目录一、核心概念解析1.1 单点登录(SSO)1.2 自动登录(Remember Me)二、代码分析三、

C#中lock关键字的使用小结

《C#中lock关键字的使用小结》在C#中,lock关键字用于确保当一个线程位于给定实例的代码块中时,其他线程无法访问同一实例的该代码块,下面就来介绍一下lock关键字的使用... 目录使用方式工作原理注意事项示例代码为什么不能lock值类型在C#中,lock关键字用于确保当一个线程位于给定实例的代码块中时