C#实现导出Word图表通用方法之散点图

目录

特殊之处

设计方案

[为什么用 Json 过渡](#为什么用 Json 过渡)

开发环境

关键代码实现

(1)创建Json格式

(2)查找图表并设置单元格数字格式

小结

特殊之处

《C#实现数据导出任一Word图表的通用呈现方法及一些体会》一文讲解了数据导出生成 WORD 图表的通用方法, 根据应用需求,大部分的图表(如柱形图)如下设置:

插入后,微缩版 Excel 应用显示了一些通用的数据设置,类别项和系列项的数值,改变其中的项和系列值,图表就会产生相应的变化。

对于像单一数据来源的,如雷达图:

可以设置必要项,分类和系列1,而不会因为系列2的数据存在而出现错误,WORD会自动"忽略"。但最近在新需求应用中,使用一款散点类型图表时发现了一些问题,其EXCEL数据源如下:

与柱状图、饼状图的格式不同,散点图是 X值和Y值的列名+二维数组设置。但实际的操作实现可能需要进行类似如下的设置:

即模拟去掉列名,直接输出数据,可正常进行显示输出 ,因此我们需要对通用方法进行一些改造。

设计方案

仍遵循原设计方案基础,将业务数据存入一个二维字符串数组,并转化为Json数据格式,并添加一个查找关键字节点,假设为" t:chart1"。在 Word 模板插入散点图图表,图表的标题设置为Json对应的查找关键字,即" t:chart1"。最后,读取Word模板文件,遍历模板文件中的图表对象,并按查找关键字与图表的标题进行对比,匹配成功,则将JSON中数组转化为图表需要的EXCEL数组形式,完成图表绘制输出。

经调试,首列单元格需要设置为数字格式,因些增加了Json配置 " t:chart1_ex ",例如下代码:

cs 复制代码
            writer.WritePropertyName("t:chart1_ex");
            writer.WriteStartArray();
            writer.WriteStartObject();
            writer.WritePropertyName("NumberFormat");
            writer.WriteValue("#,##0");
            writer.WriteEndObject();
            writer.WriteEndArray();
为什么用 Json 过渡

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

开发环境

操作系统:Windows Server 2019 DataCenter

开发工具:VisualStudio2019

框架及语言:.net 4.7.1 C#

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

关键代码实现

假设文件模板中的散点图表如下,关键查找字(图表标题)设为 " t:chart1",如下图:

(1)创建Json格式

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

cs 复制代码
StringWriter sw = new StringWriter();
        using (Newtonsoft.Json.JsonWriter writer = new Newtonsoft.Json.JsonTextWriter(sw))
        {
            writer.Formatting = Newtonsoft.Json.Formatting.Indented;
            writer.WriteStartObject();

            //t:chart1
            writer.WritePropertyName("t:chart1");
            writer.WriteStartArray();
            //point1
            writer.WriteStartObject();
            writer.WritePropertyName("y");
            writer.WriteValue((int)y);
            writer.WritePropertyName("x");
            writer.WriteValue((int)x);
            writer.WriteEndObject();

            writer.WriteStartObject();
            writer.WriteEndArray();

            writer.WritePropertyName("t:chart1_ex");
            writer.WriteStartArray();
            writer.WriteStartObject();
            writer.WritePropertyName("NumberFormat");
            writer.WriteValue("#,##0");
            writer.WriteEndObject();
            writer.WriteEndArray();

            //t:chart1



            writer.WriteEndObject();
            writer.Flush();
        }
        sw.Close();
        string jsonContent = sw.GetStringBuilder().ToString();

(2)查找图表并设置单元格数字格式

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

cs 复制代码
////一些引用
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 里的 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 包含图表,则继续
    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) //如果包含关键字则继续
        {
            //替换掉关键字,保留下来的是真正的标题
           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;

           //计算附加配置,设置单元格数字格式字串
           string numberFormat = "#,##0.0";
           if (jObject[_name + "_ex"] != null)
           {
                if (jObject[_name + "_ex"][0]["NumberFormat"] != null)
                {
                  numberFormat = jObject[_name + "_ex"][0]["NumberFormat"].ToString();
                }
           }


           //遍历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).NumberFormat = numberFormat;
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的提供能力,需要细心的测试、调试,比如

shape.Chart.ChartData.Workbook.Worksheets[1].Range("A1:Z100").Value = ""; 的初始化,散点图首列的设置等。

如有理解错误或更好的解决方案,欢迎大家讨论分享,批评指正。

相关推荐
历程里程碑2 小时前
Linux 2 指令(2)进阶:内置与外置命令解析
linux·运维·服务器·c语言·开发语言·数据结构·ubuntu
王燕龙(大卫)2 小时前
rust入门
开发语言·rust
无心水2 小时前
2、Go语言源码文件组织与命令源码文件实战指南
开发语言·人工智能·后端·机器学习·golang·go·gopath
2501_944526422 小时前
Flutter for OpenHarmony 万能游戏库App实战 - 主题切换实现
android·开发语言·javascript·python·flutter·游戏·django
张张努力变强2 小时前
C++ 类和对象(三):拷贝构造函数与赋值运算符重载之核心实现
开发语言·c++
福楠2 小时前
C++ STL | set、multiset
c语言·开发语言·数据结构·c++·算法
一晌小贪欢2 小时前
Python 健壮性进阶:精通 TCP/IP 网络编程与 requirements.txt 的最佳实践
开发语言·网络·python·网络协议·tcp/ip·python基础·python小白
enfpZZ小狗2 小时前
基于C++的反射机制探索
开发语言·c++·算法
曹牧2 小时前
C#:WebReference
开发语言·c#