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、提升设计能力、业务处理能力和总结学习方法尤为关键。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/117777.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

关于报错java.util.ConcurrentModificationException: null的源码分析和解决

一般有这种问题,方法中至少会有List或者Map下的至少两个子类,有可能参数类型相同,也有可能不同都有可能触发这个问题!其主要原因是使用了ArrayList进行删除操作或者使用iterator遍历集合的同时对集合进行修改都有可能会出现这个问题 ArrayList属于List下的子类 需要区分的是Li…

qt的一些自绘控件

https://download.csdn.net/download/venice0708/88469835

嵌入式Linux_学习路线+基础知识

嵌入式Linux_学习路线基础知识 一、学习路线 说明&#xff1a;u-boot是一大块学起来需要三到六个月比较耗时&#xff0c;也属于比较落后的知识点&#xff0c;所以暂时不学习&#xff0c;内核也是如此暂时不学习&#xff0c;从应用层入手&#xff0c;先入门再深入 二、Shell指令…

MongoDB 的集群架构与设计

一、前言 MongoDB 有三种集群架构模式&#xff0c;分别为主从复制&#xff08;Master-Slaver&#xff09;、副本集&#xff08;Replica Set&#xff09;和分片&#xff08;Sharding&#xff09;模式。 Master-Slaver 是一种主从复制的模式&#xff0c;目前已经不推荐使用。Re…

ARM | 传感器必要总线IIC

IIC总线介绍 1.谈谈你对IIC总线理解&#xff1f; 1&#xff09;IIC总线是串行半双工同步总线,主要用于连接整体电路 2&#xff09;SCL/SDA作用:IIC是两线制,一根是时钟线SCK,用于控制什么时候进行进行数据传输,时钟信号由主机发出; 另一根是数据线SDA,用于进行数据传输,可以从…

C# Winform编程(9)网络编程

网络编程 HTTP网络编程IPAddress IP地址类WebClient类WebRequest类和WebResponse类 WebBrowser网页浏览器控件TCP网络编程TcpClient类TcpListener类NetworkStream类Socket类 HTTP网络编程 IPAddress IP地址类 IPAddress类代表IP地址&#xff0c;可在十进制表示法和实际的整数…

N——>BatchSize 数据维度理解和处理(chun, cat, squeeze, unsqueeze)

数据处理之N——>BatchSize N——>batch_size train_data TensorDataset(torch.Tensor(x_train).double(), torch.Tensor(y_train).double()) train_loader DataLoader(train_data, batch_sizeargs.bs, shuffleTrue, drop_lastTrue) for batch_idx, (inputs, results…

代码随想录算法训练营第一天| 704. 二分查找、27. 移除元素。

用JAVA代码编写 704. 二分查找 给定一个 n 个元素有序的&#xff08;升序&#xff09;整型数组 nums 和一个目标值 target &#xff0c;写一个函数搜索 nums 中的 target&#xff0c;如果目标值存在返回下标&#xff0c;否则返回 -1。 示例 1: 输入: nums [-1,0,3,5,9,12]…

Haproxy 服务

Haproxy&#xff1a;他也是常用的负载均衡软件 nginx 支持四层转发&#xff0c;七层转发 haproxy 也是四层和七层转发 LVS的DR和NAT都是基于四层转发 都是基于流量的转发。 tun:四层和七层都有。 基于四层的转发&#xff1a; 1&#xff0c;lvs 2&#xff0c;nginx 3&…

JAVA代码审计-纵向越权漏洞分析

查看这个cms系统后台管理员 添加用户的页面 点击添加管理员 这个模块只有管理员拥有&#xff0c;普通用户没有这个模块。 打开源码分析是否存在越权漏洞。 ------------------------------------------------------------------------------------------------------------ …

对python中切片详解

嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 Python中什么可以切片 Python中符合序列的有序序列都支持切片(slice) 如:列表,字符,元祖 &#x1f447; &#x1f447; &#x1f447; 更多精彩机密、教程&#xff0c;尽在下方&#xff0c;赶紧点击了解吧~ python源码、视…

Python 自动化(十五)请求和响应

准备工作 将不同day下的代码分目录管理&#xff0c;方便后续复习查阅 (testenv) [rootlocalhost projects]# ls mysite1 (testenv) [rootlocalhost projects]# mkdir day01 day02 (testenv) [rootlocalhost projects]# cp -rf mysite1/ day01/ (testenv) [rootlocalhost proj…

Spring Cloud Config

Spring Cloud Config 服务端:一个集中化配置中心,可以是一个独立的服务,也可以注册到服务治理中心,它可以集中管理各个 微服务的配置; 作用原理是从某个地方读取(本地/云端)提供给其客户端作为配置; 客户端:作为一个服务端,通过读取Config的服务端来获取自己的配置文件; 服务…

计算机视觉中的数据预处理与模型训练技巧总结

计算机视觉主要问题有图像分类、目标检测和图像分割等。针对图像分类任务&#xff0c;提升准确率的方法路线有两条&#xff0c;一个是模型的修改&#xff0c;另一个是各种数据处理和训练的技巧(tricks)。图像分类中的各种技巧对于目标检测、图像分割等任务也有很好的作用&#…

【AGC】更新应用信息报未知错误解决方法

【问题描述】 最近有几个开发者遇到了一个问题&#xff0c;他们在AGC控制台配置好应用信息的图标和截图之后&#xff0c;点击保存按钮会弹出“未知错误&#xff0c;请稍后再试”的异常报错&#xff0c;导致无法正确保存应用配置信息。 出错页面如图所示。 ​​ 【解决方案】 …

debian、ubuntu打包deb包工具,图形界面deb打包工具mkdeb

debian、ubuntu打包deb包工具&#xff0c;图形界面deb打包工具mkdeb&#xff0c;目前版本1.0 下载地址&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1QX6jXNMYRybI9Cx-1N_1xw?pwd8888 md5&#xff1a; b6c6658408226a8d1a92a7cf93834e66 mkdeb_1.0-1_all.deb

ES6之Set集合(通俗易懂,含实践)

Set是什么&#xff1f;它的方法有哪些&#xff1f;它在实例开发中有什么作用&#xff1f; 让我为大家介绍一下吧&#xff01; ES6提供了新的数据结构 Set(集合) 。它类似于数组&#xff0c;但成员的值是唯一的。 创建方法&#xff1a; let s new Set()console.log(s)它属于什…

一套成熟的ERP系统,应具备哪些能力?

随着制造业数字化、智能化的不断升级&#xff0c;企业的金字塔静态管理模式也在向扁平化动态管理模式转变&#xff0c;而企业管理系统则是推动这一趋势发展的重要载体。 为了更好地实现企业管理信息化&#xff0c;越来越多的企业在应用ERP系统。ERP对企业经营起着至关重要的辅…

计时的vue写法

<el-input type"text" id"timetext" value"00时00分00秒" readonly></el-input> <el-button type"button" click"start()">开始</el-button> <el-button type"button" click"st…

el-input无法输入的问题和表单验证失败问题(亲测有效)-开发bug总结4

大部分无法输入的问题&#xff1a;基本都是没有进行v-model双向数据绑定&#xff0c;这个很好解决。 本人项目中遇到的bug问题如下&#xff1a; 点击添加&#xff0c;表单内可输入用户名 和 用户姓名&#xff0c;但有时会偶发出现无法这两个input框里面无法输入内容。 原因&a…