VSTO学习笔记(七)基于WPF的Excel分析、转换小程序

简介: 原文:VSTO学习笔记(七)基于WPF的Excel分析、转换小程序近期因为工作的需要,要批量处理Excel文件,于是写了一个小程序,来提升工作效率。 小程序的功能是对Excel进行一些分析、验证,然后进行转换。
原文: VSTO学习笔记(七)基于WPF的Excel分析、转换小程序

近期因为工作的需要,要批量处理Excel文件,于是写了一个小程序,来提升工作效率。

小程序的功能是对Excel进行一些分析、验证,然后进行转换。

 

  1. 概述
  1. 小程序主界面如下:

首先选择一个日期和类别,从命名上对待分析的Excel文件进行过滤。点击【浏览】选择待分析的Excel文件所在的目录,

程序中会获取所有子目录。然后点击【执行分析】就会按照左边CheckBox中的选择进行分析,分析结果显示在每一行中间。【修改配置】可以对分析规则进行设置,此处还未完善,后续再改进。分析结束后点击【执行转换】可以将Exce转换为我们需要的格式和内容,去除只读、重命名、转换格式、去除保护、去除公式、显示零设置等。

 

  1. 主要对Excel做如下几种分析:

只读分析:判断Excel的文件属性是否为只读;

命名分析:判断Excel的文件名是否符合既定规范,包含的工作表是否在有效值列表中;

格式分析:判断Excel的格式是否符合既定规范(是哪一种Excel,2003?2007?)

保护分析:判断Excel是否包含工作簿保护、工作表保护;

公式分析:判断Excel的单元格中是否包含公式;

显示零分析:判断Excel中的工作表是否设置为零值显示。

 

前五种都比较好理解,只有最后一种"显示零分析"需要介绍一下。

"显示零"是指什么呢?在Excel中,若单元格中包含的数值为0,则可以通过设置来决定0值是否显示,这种设置在某些情况下是有意义的。以Excel 2010为例,我们来看一下怎么设置"零值显示" :

2.1、随便打开一个Excel 2010文件,点击【File】 -->【Options】:

 

2.2、在【Advanced】选项卡中,找到【Display options for this worksheet】,选中或取消【Show a zero in cells that have zero value】,点击【OK】即可:

注意这种设置是工作表级别的,即当前设置只对所选择工作表有效。为什么要单独做这种分析呢?若一个工作表中有大量的区域都包含零值,此时若设置为零值显示则会使得工作表的内容非常杂乱,影响美观;其他情况下我们设置为零值显示,这样方便程序处理,即读取或写入单元格中的数值。

 

  1. 分析完成后会显示出分析结果,并可以【查看明细】按钮来查看具体的内容,可以精确到单元格,如XX单元格包含公式,公式是什么;XX工作表包含保护等。其中公式的分析结果会记录日志,方便查看。

 

  1. 程序中有一个全局配置文件GolbalConfig.xml,来存储分析规则,其他的一些配置信息等:

 

  1. img_405b18b4b6584ae338e0f6ecaf736533.gif 代码
    <? xml version="1.0" encoding="utf-8"  ?>
    < ExcelValidate >
        
    < SelectedDate ></ SelectedDate >
        
    < ReportKind >
            
    < Report  name ="不限" ></ Report >
            
    < Report  name ="储备" ></ Report >
            
    < Report  name ="吨煤" ></ Report >
            
    < Report  name ="汇总" ></ Report >
        
    </ ReportKind >
        
    < ReportNames >
            
    < ReportName  IsSum ="true"  reg ="^\d{1,2}月汇总表.xlsx?$" >
                
    < SheetNames >
                    
    < SheetName  name ="累计库存" ></ SheetName >
                    
    < SheetName  name ="累计消耗" ></ SheetName >
                    
    < SheetName  name ="原煤1 " ></ SheetName >
                    
    < SheetName  name ="各业务科平均库存储备资金统计 " ></ SheetName >
                    
    < SheetName  name ="各业务科库存储备资金统计" ></ SheetName >
                    
    < SheetName  name ="累计库存资金统计报表" ></ SheetName >
                    
    < SheetName  name ="累计消耗资金统计报表" ></ SheetName >
                    
    < SheetName  name ="本月库存资金统计报表" ></ SheetName >
                    
    < SheetName  name ="本月消耗资金统计报表" ></ SheetName >
                    
    < SheetName  name ="库存储备资金完成情况统计表" ></ SheetName >
                    
    < SheetName  name ="各单位平均库存储备资金统计表" ></ SheetName >
                    
    < SheetName  name ="各单位吨煤材料费" ></ SheetName >
                    
    < SheetName  name ="各业务科吨煤材料费" ></ SheetName >
                    
    < SheetName  name ="产量进尺表" ></ SheetName >
                    
    < SheetName  name ="全局1表" ></ SheetName >
                    
    < SheetName  name ="煤业1表" ></ SheetName >
                    
    < SheetName  name ="全局2表" ></ SheetName >
                    
    < SheetName  name ="煤业2表" ></ SheetName >
                    
    < SheetName  name ="袁庄矿1表" ></ SheetName >
                    
    < SheetName  name ="朱庄矿1表" ></ SheetName >
                    
    < SheetName  name ="杨庄矿1表" ></ SheetName >
                    
    < SheetName  name ="芦岭矿1表" ></ SheetName >
                    
    < SheetName  name ="朱仙庄1表" ></ SheetName >
                    
    < SheetName  name ="童亭矿1表" ></ SheetName >
                    
    < SheetName  name ="桃园矿1表" ></ SheetName >
                    
    < SheetName  name ="祁南矿1表" ></ SheetName >
                    
    < SheetName  name ="许疃矿1表" ></ SheetName >
                    
    < SheetName  name ="涡北矿1表" ></ SheetName >
                    
    < SheetName  name ="孙疃矿1表" ></ SheetName >
                    
    < SheetName  name ="淮选厂1表" ></ SheetName >
                    
    < SheetName  name ="临选厂1表" ></ SheetName >
                    
    < SheetName  name ="铁运处1表" ></ SheetName >
                    
    < SheetName  name ="工程处1表" ></ SheetName >
                    
    < SheetName  name ="总仓库1表" ></ SheetName >
                    
    < SheetName  name ="岱河矿1表" ></ SheetName >
                    
    < SheetName  name ="朔里矿1表" ></ SheetName >
                    
    < SheetName  name ="石台矿1表" ></ SheetName >
                    
    < SheetName  name ="临涣矿1表" ></ SheetName >
                    
    < SheetName  name ="海孜矿1表" ></ SheetName >
                    
    < SheetName  name ="刘店矿1表" ></ SheetName >
                    
    < SheetName  name ="袁庄矿2表" ></ SheetName >
                    
    < SheetName  name ="朱庄矿2表" ></ SheetName >
                    
    < SheetName  name ="杨庄矿2表" ></ SheetName >
                    
    < SheetName  name ="芦岭矿2表" ></ SheetName >
                    
    < SheetName  name ="朱仙庄2表" ></ SheetName >
                    
    < SheetName  name ="童亭矿2表" ></ SheetName >
                    
    < SheetName  name ="桃园矿2表" ></ SheetName >
                    
    < SheetName  name ="祁南矿2表" ></ SheetName >
                    
    < SheetName  name ="许疃矿2表" ></ SheetName >
                    
    < SheetName  name ="涡北矿2表" ></ SheetName >
                    
    < SheetName  name ="孙疃矿2表" ></ SheetName >
                    
    < SheetName  name ="岱河矿2表" ></ SheetName >
                    
    < SheetName  name ="朔里矿2表" ></ SheetName >
                    
    < SheetName  name ="石台矿2表" ></ SheetName >
                    
    < SheetName  name ="临涣矿2表" ></ SheetName >
                    
    < SheetName  name ="海孜矿2表" ></ SheetName >
                    
    < SheetName  name ="刘店矿2表" ></ SheetName >
                
    </ SheetNames >
            
    </ ReportName >
            
    < ReportName  IsSum ="false"  reg ="^\d{2}\w{3}(储备|吨煤)报表.xlsx?$" >
                
    < SheetNames >
                    
    < SheetName  reg ="^\d{4}.\d{2}$" ></ SheetName >
                
    </ SheetNames >
            
    </ ReportName >
        
    </ ReportNames >
        
    < MineNames >
            
    < MineName  name ="岱河矿" ></ MineName >
            
    < MineName  name ="工程处" ></ MineName >
            
    < MineName  name ="海孜矿" ></ MineName >
            
    < MineName  name ="淮选厂" ></ MineName >
            
    < MineName  name ="临涣矿" ></ MineName >
            
    < MineName  name ="临选厂" ></ MineName >
            
    < MineName  name ="刘店矿" ></ MineName >
            
    < MineName  name ="芦岭矿" ></ MineName >
            
    < MineName  name ="祁南矿" ></ MineName >
            
    < MineName  name ="石台矿" ></ MineName >
            
    < MineName  name ="朔里矿" ></ MineName >
            
    < MineName  name ="孙疃矿" ></ MineName >
            
    < MineName  name ="桃园矿" ></ MineName >
            
    < MineName  name ="铁运处" ></ MineName >
            
    < MineName  name ="童亭矿" ></ MineName >
            
    < MineName  name ="涡北矿" ></ MineName >
            
    < MineName  name ="许疃矿" ></ MineName >
            
    < MineName  name ="杨庄矿" ></ MineName >
            
    < MineName  name ="袁庄矿" ></ MineName >
            
    < MineName  name ="朱仙庄" ></ MineName >
            
    < MineName  name ="朱庄矿" ></ MineName >
            
    < MineName  name ="总仓库" ></ MineName >
        
    </ MineNames >
    </ ExcelValidate >

     

    分析细则
  1. 只读分析:用File.IsReadOnly判断即可;
  2. 命名分析:基于GolbalConfig.xml中的配置进行分析,主要是正则表达式和有效值列表;
  3. 格式分析:也是用正则表达式进行分析

 

Regex excel  =   new  Regex( @" ^.xlsx?$ " );

 

 

  1. 保护分析:我们知道Excel中的保护功能分为两种级别,一种是工作簿保护,一种是工作表保护,这里要区分对待。

在Excel COM API 中,工作簿保护用Workbook. ProtectWindows和Workbook. ProtectStructure进行判断,这两种保护有什么区别呢?还是以Excel 2010为例,在【Preview】选项卡中点击【Protect Workbook】:

可以看到Structure默认被选中:

 

我们保持选中Structure不变,输入密码、确认密码后保存,看看效果:

此时可以改变工作表窗口的大小,但是不能添加、删除、重命名、复制工作表等,右键中相应选项不可用:

另一种Windows保护恰好相反。

工作表保护可以对行、列、单元格、区域等做更细粒度的控制:

 

  1. 公式分析:用Excel COM API中的Range.HasFormula来判断;
  2. 显示零分析:这个比较特殊,我在Excel COM API中没有找到编程设置的方法,无奈使用OpenXML SDK 2.0中的方法进行验证。从Office 2007开始,Word、Excel、PowerPoint全面使用OpenXML作为存储格式,微软也相应提供了OpenXML SDK,方便开发人员处理OpenXML文档,关于OpenXML SDK的介绍,请参阅我的另一篇博文:

OpenXML SDK 2.0悄然发布

OpenXML SDK是个很好的东西,但是网上资源太少,OpenXML的架构也非常复杂,看着帮助文档中数量庞大的类有些茫然,关于OpenXML SDK我研究也不多,感兴趣的朋友可以一起讨论。

首先需要下载OpenXML SDK 2.0(可以在这里下载),其中有一个工具:OpenXML SDK 2.0 Productivity Tool,功能非常强大,可以查看OpenXML 文档的架构,比较两个OpenXML 文档架构的不同,甚至可以将OpenXML 文档反编译为C#代码。

下面我们来看一下显示零的设置在OpenXML架构中是如何显示的。Excel 中默认是显示零值的,先参照2.1、2.2中的步骤设置为零值不显示,然后打开OpenXML SDK 2.0 Productivity Tool:

 

点击【Open File】找到刚才设置为零值不显示的Excel文件,点击【Reflector Code】:

 

Ctrl + F,输入:ShowZeros查找,可以看到在SheetView类的属性里:

 

下面用OpenXML SDK 2.0中的方法进行分析:

 

img_405b18b4b6584ae338e0f6ecaf736533.gif 代码
                         using  (SpreadsheetDocument mySpreadsheet  =  SpreadsheetDocument.Open(file.FullName,  false ))
                        {
                            var book 
=  mySpreadsheet.WorkbookPart.Workbook;
                            
foreach  (WorksheetPart s  in  book.WorkbookPart.WorksheetParts)
                            {
                                
foreach  (SheetView view  in  s.Worksheet.SheetViews)
                                {
                                    
if  (view.ShowZeros  !=   null )
                                    {
                                        
if  ( ! view.ShowZeros)
                                        {
                                            
this .__int显示零单元格个数 ++ ;
                                            
this .__dic显示零.Add( this .__int显示零单元格个数, file.FullName);
                                        }
                                    }
                                }
                            }
                        }

 

执行转换

 

分析结束后,就可以执行转换了,来达到我们需要的格式、命名等要求。转换前默认备份,防止转换错误造成数据丢失,

将待分析的Excel所在的目录整个复制到指定位置。备份目录名取当前时间的年月日时分秒。

这里我引用了Microsoft.VisualBasic.dll进行复制目录操作,否则需要写一个递归函数,C#本身未提供复制目录的方法:

 

取消保护时需要输入工作簿密码、工作表密码:

 

  1. 运行效果

 

  1. 小结

本次用一个小程序的方式对Excel常见的方面进行了分析与验证,可以避免繁琐的手工处理。程序全部使用了WPF设计,主要运用了Excel COM API来对Excel进行操作,掺杂有少量的Linq to XML。程序中大量使用了foreach循环,效率有待提高,尤其是公式判断时会比较慢,你可以根据需要自行修改代码。仅供测试,在生产环境中情谨慎使用。

目录
相关文章
|
6月前
|
JavaScript Java 测试技术
基于ssm+vue.js+uniapp小程序的高质量升学分析系统附带文章和源代码部署视频讲解等
基于ssm+vue.js+uniapp小程序的高质量升学分析系统附带文章和源代码部署视频讲解等
51 3
|
2月前
|
前端开发 JavaScript Java
导出excel的两个方式:前端vue+XLSX 导出excel,vue+后端POI 导出excel,并进行分析、比较
这篇文章介绍了使用前端Vue框架结合XLSX库和后端结合Apache POI库导出Excel文件的两种方法,并对比分析了它们的优缺点。
857 0
|
2月前
|
索引 Python
Excel学习笔记(一):python读写excel,并完成计算平均成绩、成绩等级划分、每个同学分数大于70的次数、找最优成绩
这篇文章是关于如何使用Python读取Excel文件中的学生成绩数据,并进行计算平均成绩、成绩等级划分、统计分数大于70的次数以及找出最优成绩等操作的教程。
99 0
|
3月前
|
数据可视化 数据处理 Python
Python操作Excel:轻松实现数据处理与分析
Python操作Excel:轻松实现数据处理与分析
134 0
|
4月前
|
C# 开发者 Windows
WPF遇上Office:一场关于Word与Excel自动化操作的技术盛宴,从环境搭建到代码实战,看WPF如何玩转文档处理的那些事儿
【8月更文挑战第31天】Windows Presentation Foundation (WPF) 是 .NET Framework 的重要组件,以其强大的图形界面和灵活的数据绑定功能著称。本文通过具体示例代码,介绍如何在 WPF 应用中实现 Word 和 Excel 文档的自动化操作,包括文档的读取、编辑和保存等。首先创建 WPF 项目并设计用户界面,然后在 `MainWindow.xaml.cs` 中编写逻辑代码,利用 `Microsoft.Office.Interop` 命名空间实现 Office 文档的自动化处理。文章还提供了注意事项,帮助开发者避免常见问题。
308 0
|
5月前
|
小程序
【奇葩问题】微信小程序 We分析 访问来源Top10的总比例为什么不止100%
【奇葩问题】微信小程序 We分析 访问来源Top10的总比例为什么不止100%
60 3
|
5月前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp小程序的数据分析岗位招聘信息与分析附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp小程序的数据分析岗位招聘信息与分析附带文章源码部署视频讲解等
25 0
|
5月前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp小程序的高校成绩分析附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp小程序的高校成绩分析附带文章源码部署视频讲解等
44 0
|
5月前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp小程序的学生成绩分析和弱项辅助系统附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp小程序的学生成绩分析和弱项辅助系统附带文章源码部署视频讲解等
47 0
|
6月前
|
JavaScript Java 测试技术
基于ssm+vue.js+uniapp小程序的高校智能培训管理系统分析与设计附带文章和源代码设计说明文档ppt
基于ssm+vue.js+uniapp小程序的高校智能培训管理系统分析与设计附带文章和源代码设计说明文档ppt
58 1