ExcelHome技术论坛

 找回密码
 免费注册

QQ登录

只需一步,快速开始

快捷登录

搜索
EH技术汇-专业的职场技能充电站 妙哉!函数段子手趣味讲函数 Excel服务器-会Excel,做管理系统 效率神器,一键搞定繁琐工作
HR薪酬管理数字化实战 Excel 2021函数公式学习大典 Excel数据透视表实战秘技 打造核心竞争力的职场宝典
让更多数据处理,一键完成 数据工作者的案头书 免费直播课集锦 ExcelHome出品 - VBA代码宝免费下载
用ChatGPT与VBA一键搞定Excel WPS表格从入门到精通 Excel VBA经典代码实践指南
查看: 14999|回复: 49

[原创] ExcelDna,让Excel跑起来!

  [复制链接]

TA的精华主题

TA的得分主题

发表于 2022-7-9 23:14 | 显示全部楼层 |阅读模式
本帖最后由 wodewan 于 2022-7-10 11:38 编辑

前言:废话篇

曾经学习python的xlwing时,有个广告语叫xlwing,让Excel飞起来,盗版一个,ExcelDna,让Excel跑起来!
ExcelDna开发Xll可用C#,Vb.Net等,本帖使用C#,内容暂定为主体框架搭建,文件结构,函数,加载项、自定义Ribbon,窗体和任务窗格等。
水平有限,讲不了原理,也说不了优缺点,只分享如何使用,跑通流程,所以叫让Excel跑起来。。。

一、 主体框架搭建
1. 首先要说说IDE,如果是VS2019可使用ExcelDna插件模板自动生成主体框架,无须任何配置(后续可能会提到)。因本贴使用的是Visual Studio 2022,VS2022暂不支持ExcelDna的该插件,所以将使用Nuget包的方式一步一步手动搭建框架,虽然稍显麻烦,但对初次使用ExcelDNA也有好处,可以更加清楚的了解ExcelDNA的项目结构和每个文件的作用,熟练之后也可自行生成模板,免去一些重复的步骤。

2. 先建立一个C#或Vb.Net的类库项目,这里选.NetFrameWork,  .Net 的有可能后面再说,毕竟从预览版的ExcelDna1.6开始是支持.Net的,步骤如下图:如果不要支持一些老版本和系统,.NetFrameWork的版本建议选4.6.2之后版本,如下图所示,将生成一个空的C#或Vb.Net类库项目。
1.gif


3. 接着使用Nuget工具安装ExcelDna包,操作步骤如下,需要注意的是,初次使用推荐ExcelDna.Addin 1.1.1版本。最新的1.5.1版本的貌似有点问题(可至官网了解详情,和杀软有关),预览版的1.6解决了这个问题,但1.6版本是包含.Net版本的,这里用不到。安装ExcelDna.Addin 1.1.1(插件包)的过程中会同时安装ExcelDna.Intergration 1.1.0(主要的Excel交互包),步骤如下:
2.gif

4. 安装完成后,整个项目文件应该如下图所示,标号1:为说明文档,英文水平好的可以看看,也没什么大用(个人意见);标号2:该引用比较关键,后续的很多功能都要使用到,但这里只要确认有自动添加了该引用即可,仅此而已;标号3:一个以DNA为后缀名的文件,后期一些dll的集成和设置会用到,暂时也不用管,只要确认有自动添加了即可:
image.jpg

5. 主框架已经基本搭建完成,可以试着写一个简单的函数来验证一下,然后调试运行,包括生成的bin文件夹内的文件,这里不做详细解释,待函数部分再说,只验证流程是否能够跑通即可,步骤如下:
3.gif

6. 顺便说一下bin文件夹下的各个文件的用途,bin文件夹下有2个文件夹,一个debug,一个release,分别对应着解决方案配置的内容,如下图:至此,一个简单的ExcelDna框架已经搭建好了,就可以使用C#或VB.Net写xll插件了,后续可以根据各种需求,实现不同的功能模块,编译后2个带packed后缀的文件就是最后打包好的32位和64位的xll文件,可直接使用,未完待续。。。
image.jpg






补充内容 (2024-9-16 16:57):
新版SDK样式的ExcelDna入门系列教程可参考B站视频:【快速搭建基于ExcelDna的Excel插件项目】 https://www.bilibili.com/video/BV1QV4rebE75

评分

11

查看全部评分

TA的精华主题

TA的得分主题

 楼主| 发表于 2022-7-9 23:15 | 显示全部楼层
本帖最后由 wodewan 于 2022-7-10 20:40 编辑

吐槽一下,代码文字模式有点麻烦,代码贴进去的和显示的不一样,可能只能识别VBA吧?编辑了好几回,估计老大审核的也烦了。。。。
但就是显示不全,算了,不知道第几次编辑了,只能删掉代码模式,直接贴文字了,或直接下载附件的动图查看。。。。。

二、 函数和加载项
这两个为啥放一起?因为这两个看着虽然差异很大,但用法和语法上基本差不多,就放一起来说。

1. 函数
说到ExcelDna的函数,可能是ExcelDna可以说的最多的内容了,什么C API,什么异步,什么RTD,等等,看着就让人头大,但其实对于刚开始接触ExcelDna来说,完全可以先把这些全部忘掉,先跑通流程,那些高级功能可以之后用到了再一个一个来学,其实日常能用到的也没几个,哈哈,广告来一遍,先让ExcelDna跑起来。。。,怎么让ExcelDna的函数跑起来呢?其实很简单,1楼最后其实有个动图演示,这里文字说明一下,简单3步即可:

第一步:既然要使用ExcelDna,为了区分功能,删掉自动生成的class1,自行先添加一个类Myfunction,并将这个类的签名变成Public static class 函数名,代码如下:

public static class MyFunction
{
}

第二步:在这个公开的静态类中就可以写函数了,函数的签名同样也是公开的和静态的,这里就先写一个简单的SayHello函数,代码就演变成这样:

public static class MyFunction
{
    public static string SayHello()
    {
      return "Hello ExcelDna";
    }
}

第三步:完成了以上3步,在Excel中并不能看到这个函数,使用得告诉Excel这个函数是个Excel函数,方法很简单就是在这个函数上加个”特性“,至于什么是特性,如果你不知道得化,无所谓,你就当它是个标签,贴上这个标签,这个函数就会具有一些特殊得功能,这里我们就给这个函数贴个[ExcelFunction]的标签,至于这个标签中的一些参数,刚开始也可以先不管,用到了再查文档,还是那句化先让程序跑起来,这样代码就演变成这样:

public static class MyFunction
{
    [ExcelFunction]
    public static string SayHello()
    {
          return "Hello ExcelDna";
    }
}

到此为止,一个简单的ExcelDNA函数就搞定了,你可以编译一下,或调试运行一下,就可以,发现出错了。。。
等等,好像漏掉了点什么?仔细的朋友可以发现,在1楼的动图中好像using了个什么东西,那是干啥的,有啥用呢?
你肯定没忘记,第三步我们加了个特性 [ExcelFunction],那程序怎么才能告诉Excel我加了个特性呢,就靠那个using ExcelDna.Integration;
而特性 [ExcelFunction]就是这个dll其中的一项功能,所以使用前,必须先Using一下,类似其他语言里的import,导入的意思。

题外话,其实现在的IDE的提示很智能,如果你忘记Using,会在对应的地方出现红色的波浪线,你只需鼠标放置到那里,然后按Alt+Enter就会自动提示引用。

但这个函数也太简单了点吧?到目前为止,我们其实并没有和Excel程序的对象(workbook,sheet,range等)做任何的交互,这个时候就要引入Excel的编程接口了,对应Exceldna来说有2个接口可用,一个C API接口,一个Com 自动化接口,我们选择后者?what?后者,不是说前者才是ExcelDna的灵魂吗?(备注:以下仅为个人瞎掰,可忽略,因为自己也不太会用Excel的C API,哈哈)却是如此,不过我们的口号是什么?让EXCELDNA跑起来,对于前者来说是为效率而生,而且如果你已经熟练掌握了C API,估计也没必要用ExcelDNA了,也许C++更合适了,所以如果想快速入门,选择后者会更友好,毕竟对于Excel的调用,大部分语法和关键字和VBA如出一辙。。。。。。

说了这么多,那我们怎么和Excel交互呢,即使不管C Api接口,Com自动化接口又是个啥呢?答案是你也不用管,最简单的方法,直接Nuget安装一个ExcelDna.Interop,安装完成后你会发现引用中多了一个Microsoft.Office.Interop.Excel,这个就可以理解为我们与Excel交互的接口了,方法如下,:
4.gif

至此,ExcelDna包的3个最主要的包我们都已经安装完成,分别是ExcelDna.Addin,ExcelDna.Intergration,ExcelDna.Interop,前两个我们已经大概接触过了(ExcelDna.Addin以后有可能会单说),第三个我们还没用过,下面我们就在加载项中试着使用一下,顺便介绍一下加载项的用法,还记得之前例子的函数吗?如果现在我们想让函数计算的结果通过按钮的方式写入Excel特定的单元格,而不是以函数的方式返回,怎么办?

2. 加载项
上面说了要和Excel交互的引用一下Excel的对象模型,通过安装ExcelDna.Interop,我们其实已经添加了Excel的对象模型,那如果使用呢,下面就通过Excel加载项来说明,和函数一样,我们同样分三步:

第一步:为了区别函数,我们新建一个类,命名为MyCommand,并标识为Public和Static,并添加相关引用,代码如下:

using ExcelDna.Integration;
using Excel = Microsoft.Office.Interop.Excel;
public static class MyCommand
{
}

第二步:在这个公开的静态类中也写一个函数,函数的签名同样也是公开的和静态的,注意下,返回值是Void,因为执行的是一个过程,无须返回值,这里就先写一个简单的WriteToExcel函数,需要说明的是,申明的 Excel.Application App ,这个App就代表着Excel程序了,而这个实例对象就是ExcelDnaUtil.Application,代表着当前插件所在的Excel程序,所以第二行代码代码App.ActiveCell.Value2的意思就是,当前插件所在Excel程序的激活的单元格的值等于”Hello ExcelDNA“,代码就演变成这样

using ExcelDna.Integration;
using Excel = Microsoft.Office.Interop.Excel;

public static class MyCommand
{
    public static void WriteToExcel()
    {
            Excel.Application App = ExcelDnaUtil.Application as Excel.Application;
    }
}

第三步:第三步就简单了,和函数一样,给这个函数贴上标签,告诉程序这个是个加载项, [ExcelCommand(MenuName ="Test",MenuText ="Write To excel")];是不是和UDF函数很类似,只是由[ExcelFunction]改成了[ExcelCommand]并且加了2个参数,这两个参数只是菜单的名称而已。代码就变成了这样:

using ExcelDna.Integration;
using Excel = Microsoft.Office.Interop.Excel;

public static class MyCommand
{
    [ExcelCommand(MenuName ="Test",MenuText ="Write To excel")]
    public static void WriteToExcel()
    {
        Excel.Application App = ExcelDnaUtil.Application as Excel.Application;
        App.ActiveCell.Value2 = "Hello ExcelDNA";
    }
}

整个过程演示如下,因为动图超过2m了就打了包,有兴趣的可以下载后查看:
至此,已经完成了函数和加载项的流程跑通,下一个就是UI界面了,未完待续。。。

MyCommand演示.7z

1.83 MB, 下载次数: 188

评分

2

查看全部评分

TA的精华主题

TA的得分主题

 楼主| 发表于 2022-7-9 23:16 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
本帖最后由 wodewan 于 2022-7-11 17:55 编辑

三、Ribbon 功能区

要实现一个Ribbon功能区,分2部分逐一说明:
1. 自定义RibbonXml的编写
2. 自定义RibbonClass类的编写

先说第一部分,RibbonXml的编写,看下图:
1. LoadImage回调,用于Ribbon区图片显示的回调,会在Ribbon类中重写;
2. button按钮中的Image标签,用于指定按钮的图片名称,不带后缀名;
3. OnAction回调,用于button按钮的点击回调;
这里主要说明一下架构方式,本例使用的是通过添加资源来实现,xml代码脱离类代码文件,好处一目了然,可以动态替换,并且编辑XML可以智能提示,做法也很简单,先新建一个RibbonResources的文件夹,将图片资源放进去了,然后再添加一个xml文件(可以新键,或导入已编辑好的xml均可);然后在项目中再添加一个资源文件,将刚刚资源文件夹内的图片资源和xml文件作为资源添加即可。完成了这些步骤,写Ribbon类时就会方便很多。
image.jpg

再说第二部分,RibbonClass类的编写,如下图:
1. 对于Ribbon类的编写和函数以及加载项不同,标注是[ComVisible(true)],也就是Com可见,同时继承: ExcelRibbon类
2. 第一个重写的函数GetCustomUI,用于获取第一部分资源文件中的RibbonXML内容
3. 第二个重写的函数LoadImage,用于获取第一部分资源文件中的图片内容
4. OnAction回调,这个不用多说,就是按钮点击后的执行的程序,一般我们的逻辑都在这个回调中完成。
image.jpg

至此一个Ribbon功能区大体就完成了,也许有人获取会问,为啥搞这么复杂,直接将xml代码都写Ribbon类里不就得了?举个例子:目前Ribbon区只有一个按钮Start,如果需要新增一个按钮Stop得话,使用这样的架构就非常得容易了,基本无须改变Ribbon类的代码,只需在Xml中指定即可,目的就是做到代码与Xml,图片这些资源分离,降低耦合,各做各的事,Ribbon类负责逻辑,Xml负责UI,简单演示如下:
RibbonTest.gif

好了,先写到这吧,根据2楼的经验,这次就不贴代码了,太麻烦了,上面的两部分图片在截图时考虑到不贴代码均截取完整,有兴趣的可以参考一下,至于窗体和任务窗格,其实到了这一步后就简单了,无非是新增窗体或窗格后,通过回调调用而已,还有一些比如Addin类的作用,如何嵌入第三方dll等一些小细节,看情况再说吧。。。


TA的精华主题

TA的得分主题

 楼主| 发表于 2022-7-9 23:24 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
本帖最后由 wodewan 于 2022-7-14 12:05 编辑

补充:关于配置文件的说明

image.png

ExcelDna有个很有用的配置文件,以.Dna为文件后缀名的文件,每个项目都存在这个文件,上图是一个标准的DNA配置文件格式,一般情况下,无须更改该文件,但对于一些特殊情况,这个文件的作用将非常大,主要功能如下,其中 <ExternalLibrary  /> 表示的对创建的主dll的设置, <Reference  />表示对引用的dll的设置,IncludePdb参数,是否输出调试符号文件(暂时可忽略),LoadFromBytes(默认即可),主要说下面3种情况:
1. ExplicitExports:控制函数的输出方式
2. [size=1em]ExplicitRegistration:控制函数的注册方式
3. Pack:控制dll的打包方式(后面的例子中有介绍)

1. 控制函数的输出方式(ExplicitExports)

何为控制函数的输出方式,前面说过,要在Excel中使用UDF的话,必须在函数上使用特性[ExcelFunction]标注,真的是这样的吗?做个测试,再添加一个函数叫SayHello2,代码如下:
image.png
可以看到,在默认情况下只要是public static修饰的函数,无论加不加[ExcelFunction]标注,均可在Excel中使用该函数,但大多数情况下我们并不想让所有的public static修饰的函数都在Excel中显示,有些可能只是内部调用的函数,怎么办呢?答案就是使用这个配置文件中的 ExplicitExports="true",这样只有标注了[ExcelFunction]标签的函数才会显示,如下图:
image.jpg

2. 控制函数的注册方式([size=1em]ExplicitRegistration)
[size=1em]
这个参数默认值是false,也就是自动注册的,在一些特殊需求时确作用很大,举个例子,如果一个函数在运行前需要对函数的某个功能进行修改,也就是说可以实现函数功能动态修改,而无须修改原函数代码,这时将参数[size=1em]ExplicitRegistration=True
然后进行手动注册,手动注册相对比较麻烦,而且涉及到DNA的另外一个包ExcelRegistration,这里简单介绍一下步骤,日常应用不常见,不具体展开:
1. 自定义一个Myaddin的类,并实现IExcelAddin接口;
2. 在实现这个类的AutoOpen方法中进行手动注册;
3. 调用ExcelRegistration中的GetExcelFunctions方法获取函数
4. 获取到函数后使用FunctionAttribute获取函数属性进行修改,最后返回

3. 控制第三方dll的打包方式
这个用的比较多,因后面的例子中有详细的介绍,这里就不多说了,主要的语法如下:

<Reference Path="xxx.dll" Pack="true"/>
  3.1 其中xxx.dll为第三方包的dll名(如QrCode.dll)
  3.2 Pack=“true” 表示将该dll打包嵌入到xll文件中。


评分

2

查看全部评分

TA的精华主题

TA的得分主题

发表于 2022-7-12 23:32 | 显示全部楼层
本帖最后由 momo66666 于 2022-7-13 15:21 编辑

学习了~~~

TA的精华主题

TA的得分主题

 楼主| 发表于 2022-7-13 14:22 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
本帖最后由 wodewan 于 2022-7-13 23:26 编辑

四、窗体和任务窗格的应用(做一个简单的二维码插件)
上面的例子中,在Ribbon功能区,设定了2个按钮,这次就改造这两个按钮一个显示窗体,一个显示任务窗格。
大体步骤如下:
1. 做一个Usercontrol,名称为UI_QR,供窗体和任务窗格调用。



2. 使用Nuget安装QrCode第三方包,并在UI_QR中实现相关逻辑。
    这里简单的使用文本框的change事件来触发生成二维码,然后将获取到的图片导入到picturebox中即可,代码很少,除了using语句后面会贴出,所有代码如下图:


3. 添加窗体和任务窗格,并将UI_QR加入其中即可。
UI的相关逻辑多已经做好,剩下的就简单了,将UI加入窗体和窗格中即可,两者是通用的。
<1>. 窗体,直接用图形化工具在窗体上画出来即可,然后使用如下代码直接调用
image.png

<2>. 任务窗格
直接在MyRibbon类中使用DNA的CreateCustomTaskPane创建并显示任务窗格,代码如下:
image.png

好了运行一下试试,窗体运行成功,任务窗格失败
image.png
为什么呢?任务窗格其实和Ribbon功能区一样,都是Com来实现的,所以和Ribbon一样,需在UI上做个标记,还记得上面没贴全的using语句代码吗?这里就包含了要添加的这个[ComVisible(true)],不可少

image.png

下面再运行试验一下,没问题可以正常运行:
1.gif

有小伙伴可能会问二维码可以加个中心logo吗?这个不是EXCELDNA的内容了,其实只需将图片位置加入参数即可,很方便,再运行一下。
image.png
2.gif

又有小伙伴疑惑了,为啥说这个,这就是为什么选择ExcelDna的原因了,因为它是Excel和其他语言第三方库的桥梁,通过它可以很轻松的使用其他语言的第三方库的功能,这就极大的扩展了Excel插件的功能,如上所示就这么简简单单的几步,就可以实现一个二维码的功能。
最后说下部署,xll的部署前面已经说过了,基本上你copy那个packed后缀的xll就行,但这是对于没有引入第三方包的情况,如果像目前这个情况,引入了QRCODE这个第三方包,若只copy那个xll就不行了,就必须把QRCoder.dll这个文件放置在xll旁边就行,那有没有办法把第三方包的dll也集成到xll文件中去,成为单文件插件,无须其他dll呢?答案是肯定的,这就用到了那个dna后缀名的文件,方法如下:
3.gif


这样就可以将xll放到任意位置进行加载,而无须在旁边放第三方的dll了。


水平有限,难免有误,还请指正,断断续续花了几天的时间,希望给刚接触ExcelDna,有兴趣使用的朋友一个参考。


image.png

TA的精华主题

TA的得分主题

发表于 2022-7-13 23:41 | 显示全部楼层

TA的精华主题

TA的得分主题

发表于 2022-7-14 15:25 | 显示全部楼层
看了你的贴帖子,建一个新的线程显示窗口,为什么第二次打开的时候会异常?怎么解决呢? 05157665eb9ee6c899148bd469c52b0.png 565d0aec44aeecfd174a3dfbb03f539.png

TA的精华主题

TA的得分主题

 楼主| 发表于 2022-7-14 17:32 | 显示全部楼层
momo66666 发表于 2022-7-14 15:25
看了你的贴帖子,建一个新的线程显示窗口,为什么第二次打开的时候会异常?怎么解决呢?

wpf那个帖子?
具体不清楚第二次打开是什么意思,单看异常信息,应该和跨线程访问有关,先看看后台的Excel.EXE有没有正常退出。方便的话,可以传个附件看看。

TA的精华主题

TA的得分主题

发表于 2022-7-14 17:52 | 显示全部楼层
wodewan 发表于 2022-7-14 17:32
wpf那个帖子?
具体不清楚第二次打开是什么意思,单看异常信息,应该和跨线程访问有关,先看看后台的Exc ...

打开窗口后关闭,再次打开,提示窗口被原来的线程占用么?附件代码,谢谢!

EXCELDNADEMO.rar

214.38 KB, 下载次数: 77

您需要登录后才可以回帖 登录 | 免费注册

本版积分规则

手机版|关于我们|联系我们|ExcelHome

GMT+8, 2024-11-21 17:15 , Processed in 0.044609 second(s), 11 queries , Gzip On, MemCache On.

Powered by Discuz! X3.4

© 1999-2023 Wooffice Inc.

沪公网安备 31011702000001号 沪ICP备11019229号-2

本论坛言论纯属发表者个人意见,任何违反国家相关法律的言论,本站将协助国家相关部门追究发言者责任!     本站特聘法律顾问:李志群律师

快速回复 返回顶部 返回列表