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

相关文章

Java中流式并行操作parallelStream的原理和使用方法

《Java中流式并行操作parallelStream的原理和使用方法》本文详细介绍了Java中的并行流(parallelStream)的原理、正确使用方法以及在实际业务中的应用案例,并指出在使用并行流... 目录Java中流式并行操作parallelStream0. 问题的产生1. 什么是parallelS

MySQL数据库双机热备的配置方法详解

《MySQL数据库双机热备的配置方法详解》在企业级应用中,数据库的高可用性和数据的安全性是至关重要的,MySQL作为最流行的开源关系型数据库管理系统之一,提供了多种方式来实现高可用性,其中双机热备(M... 目录1. 环境准备1.1 安装mysql1.2 配置MySQL1.2.1 主服务器配置1.2.2 从

C++中unordered_set哈希集合的实现

《C++中unordered_set哈希集合的实现》std::unordered_set是C++标准库中的无序关联容器,基于哈希表实现,具有元素唯一性和无序性特点,本文就来详细的介绍一下unorder... 目录一、概述二、头文件与命名空间三、常用方法与示例1. 构造与析构2. 迭代器与遍历3. 容量相关4

C++中悬垂引用(Dangling Reference) 的实现

《C++中悬垂引用(DanglingReference)的实现》C++中的悬垂引用指引用绑定的对象被销毁后引用仍存在的情况,会导致访问无效内存,下面就来详细的介绍一下产生的原因以及如何避免,感兴趣... 目录悬垂引用的产生原因1. 引用绑定到局部变量,变量超出作用域后销毁2. 引用绑定到动态分配的对象,对象

SpringBoot基于注解实现数据库字段回填的完整方案

《SpringBoot基于注解实现数据库字段回填的完整方案》这篇文章主要为大家详细介绍了SpringBoot如何基于注解实现数据库字段回填的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以了解... 目录数据库表pom.XMLRelationFieldRelationFieldMapping基础的一些代

Java HashMap的底层实现原理深度解析

《JavaHashMap的底层实现原理深度解析》HashMap基于数组+链表+红黑树结构,通过哈希算法和扩容机制优化性能,负载因子与树化阈值平衡效率,是Java开发必备的高效数据结构,本文给大家介绍... 目录一、概述:HashMap的宏观结构二、核心数据结构解析1. 数组(桶数组)2. 链表节点(Node

Java AOP面向切面编程的概念和实现方式

《JavaAOP面向切面编程的概念和实现方式》AOP是面向切面编程,通过动态代理将横切关注点(如日志、事务)与核心业务逻辑分离,提升代码复用性和可维护性,本文给大家介绍JavaAOP面向切面编程的概... 目录一、AOP 是什么?二、AOP 的核心概念与实现方式核心概念实现方式三、Spring AOP 的关

Python版本信息获取方法详解与实战

《Python版本信息获取方法详解与实战》在Python开发中,获取Python版本号是调试、兼容性检查和版本控制的重要基础操作,本文详细介绍了如何使用sys和platform模块获取Python的主... 目录1. python版本号获取基础2. 使用sys模块获取版本信息2.1 sys模块概述2.1.1

一文解析C#中的StringSplitOptions枚举

《一文解析C#中的StringSplitOptions枚举》StringSplitOptions是C#中的一个枚举类型,用于控制string.Split()方法分割字符串时的行为,核心作用是处理分割后... 目录C#的StringSplitOptions枚举1.StringSplitOptions枚举的常用

Python实现字典转字符串的五种方法

《Python实现字典转字符串的五种方法》本文介绍了在Python中如何将字典数据结构转换为字符串格式的多种方法,首先可以通过内置的str()函数进行简单转换;其次利用ison.dumps()函数能够... 目录1、使用json模块的dumps方法:2、使用str方法:3、使用循环和字符串拼接:4、使用字符