文章目录
前言
这几天玩VBA,
尝试XML绘制EXCEL主菜单,注入EXCEL,并调用VBA的函数。
众所周知VS的IDE是多强大,我就寻思能不能用C#画界面去控制VBA的插件。这样更新就能单xlam覆盖更新了。
然后开始学习VS的 VSTO外接程序,确实很有意思。
一、第一坑:安装offic2007后excel加载项找不到了
至于为什么使用2007,我寻思07上能跑的13、16不得兼任啊。我用13的开发,还得倒回来弄07、10的兼容不是。
二、示例1 通过Ribbon XML自定义Excel主菜单并添加新项
步骤1:创建Ribbon XML定义文件
新建一个XML文件(如customUI.xml),定义以下结构
<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui">
<ribbon>
<tabs>
<tab id="CustomTab" label="我的工具">
<group id="CustomGroup" label="数据处理">
<button
id="btnCleanData"
label="数据清洗"
size="large"
onAction="DataCleaningMacro"
imageMso="DataRefreshAll" />
<menu id="mnuAdvanced" label="高级操作" imageMso="GroupEdit">
<button id="btnMerge" label="合并工作表" onAction="MergeSheetsMacro"/>
<button id="btnSplit" label="拆分数据" onAction="SplitDataMacro"/>
</menu>
</group>
</tab>
</tabs>
</ribbon>
</customUI>
关键参数说明:
imageMso:使用Office内置图标(如DataRefreshAll代表刷新图标)3
onAction:绑定VBA回调函数
步骤2:将XML集成到Excel文件
将Excel文件后缀改为.zip
在压缩包内创建路径:customUI/_rels
将customUI.xml放入customUI文件夹
创建关系文件customUI/_rels/customUI.xml.rels:
<?xml version="1.0" encoding="UTF-8"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Type="http://schemas.microsoft.com/office/2006/relationships/ui/extensibility"
Target="/customUI/customUI.xml"
Id="RibbonCustomization" />
</Relationships>
步骤3:编写VBA回调函数
在Excel的VBA编辑器中添加模块(需启用宏):
' 按钮点击事件处理
Sub DataCleaningMacro(control As IRibbonControl)
MsgBox "执行数据清洗操作", vbInformation
' 实际功能代码
End Sub
Sub MergeSheetsMacro(control As IRibbonControl)
MsgBox "执行工作表合并", vbInformation
End Sub
步骤4:验证与调试
通过Custom UI Editor工具直接注入XML(推荐)
使用IRibbonUI.Invalidate方法刷新界面:
Dim MyRibbon As IRibbonUI
Sub ribbonLoaded(ribbon As IRibbonUI)
Set MyRibbon = ribbon
End Sub
Sub RefreshRibbon()
MyRibbon.Invalidate
End Sub
二、示例1 总结
这个示例过于复杂,而且卡在第四步,我觉得应该寻找更好、更简单的办法。
三、示例2 创建VSTO外接程序
步骤1:新建解决方案和Excel VSTO 外接程序项目
创建一个Excel VSTO外接程序的新项目,选择“Excel VSTO外接程序(Visual Basic)”模板,命名为“外接程序安装”。VS将显示解决方案名称为“外接程序安装”,包含“外接程序安装”项目。
步骤2:设计Excel VSTO外接程序
主要是设计菜单项和代码。
1.在“外接程序安装”项目上,点击右键,选择“添加”——“类”,在“添加新项-外接程序安装”界面,选择“office/sharepoint”中的“功能区(可视化设计器)”,点击“添加”后默认建立“Ribbon1.vb”的模块。
2.依次右键点击“Group1”、“TabAddins(内置)”,均选择“ 删除”。
(1)点击左侧的“工具箱”,拖拽“Office 功能区控件”中的Tab按钮到“Ribbon1.vb[设计]”中,修改右侧属性的有关值,如Label改为“关于”等。
(2)再次点击左侧的“工具箱”,拖拽“Office 功能区控件”中的Group按钮到“Ribbon1.vb[设计]”的“关于”里面,修改右侧属性的有关值,如Label改为“帮助”等。
(3)点击左侧的“工具箱”,拖拽“Office 功能区控件”中的Group按钮到“Ribbon1.vb[设计]”中的帮助里面,修改右侧属性的有关值,如Label改为“版本”,ControlSize改为“RibbonControlSizeLarge”,OfficeImageId改为“Help”等。
3.双击“版本”图标,进入“Ribbon1.vb”的代码编辑界面,在Button1_Click的过程中,输入以下代码
MsgBox(“version 1.0.0.0”)
引用资料: 创建 VSTO 外接程序的windows安装包
https://blog.csdn.net/weixin_45661908/article/details/123310069
三、示例2 总结
跟着做了一遍,在第二条:MsgBox(“version 1.0.0.0”) 这里卡住了。
思路了半天,反应过来,他是用的VB,而我用的C#。 尴尬了
嗯 最终跟着做完,挺详细、简单的,安装那部分不用过分深入,了解就行。主要是熟悉外接程序的搭建。
四、示例 3 C# VSTO外接程序示例
步骤1:创建一个Excel外接程序:
步骤2:添加项,添加一个Ribbon菜单:
步骤3 : 熟悉Tab控件
RibbonTab控件是所有控件的容器
步骤4: 熟悉Group控件
Group控件的作用是将我们的功能进行分组。回到我们之前的规划,我们的歪歪插件有财经,地图,天气,关于这几大功能。所以我们需要在界面上放置4个Group控件,并对其进行命名。
步骤5 :Menu控件和SpliterButton控件
在分好类之后,我们需要对每个分类的细小功能进行设计,这里面我们需要放置各种控件,首先我们可能会接触到的就是Menu控件,里面可以包含Button,SplitButton等。
步骤6:RibbonXml
在Office中Ribbon菜单时可以通过RibbonXML进行配置,也就是说,上面的可视化界面设计其实是为我们提供了编辑RibbonXML的设计时支持。其实我们也可以直接创建一个XML文件进行设计,然后在代码中进行加载,同样能够实现这样的功能。在有些情况下,比如我们创建SharedAddin程序时,根本没有设计时支持。所以了解RibbonXML对于创建可兼容多版本Excel菜单系统显得尤为重要。
要创建RibbonXML最好的做法是对着新建的可视化菜单,然后右键->将功能区导出到XML。然后项目会自动创建Ribbon.xml和Ribbon.cs文件,其中Ribbon.xml是布局文件,Ribbon.cs是事件处理代码。
打开Ribbon.xml可以看到如下代码,可以看到这个UI界面的展现即是使用了该XML文件来进行渲染的,其中在XML中还可以声明一些事件,后面会讲。
现在,如何在我们的应用程序中加载该RibbonXML并渲染出Ribbon菜单呢?首先,我们将之前的添加的可视化设计的Ribbon菜单YYMenu.cs排除到项目外。然后转到ThisAddIn.cs中,覆写Office.IRibbonExtensibility 接口的CreateRibbonExtensibilityObject方法,实例化自动生成的Ribbon类,然后返回。
//ThisAddIn.cs
private Ribbon customerRibbon;
protected override Office.IRibbonExtensibility CreateRibbonExtensibilityObject()
{
customerRibbon = new Ribbon();
return customerRibbon;
}
运行程序,即可看到如下效果:
可以看到图片不见了,这是因为在导出功能区为XML的时候,VS没有帮我们处理图片,所以需要我们自己来添加。在RibbonXML文档中, customUI节点下有loadImage事件,Button有getImage事件和image属性。customUI节点的loadImage方法和子节点的image属性一起使用,image属性作为loadImage方法的参数。
loadImage方法如下:
//Ribbon.cs
public Image LoadImage(string imageName)
{
Assembly assembly = Assembly.GetExecutingAssembly();
//String[] all =assembly.GetManifestResourceNames();//GetResourceName
Stream stream = assembly.GetManifestResourceStream("YYAddIn.Resources." + imageName);
return Image.FromStream(stream);
}
其中,imageName即为Button控件的image属性要设置的图片名称。资源文件图片,要设置为嵌入的资源。
引用资料: 浅谈Excel开发:二 Excel 菜单系统
https://blog.csdn.net/dyllove98/article/details/9707613
四、示例 3 总结
示例3是最符合我需求的测试,它为我初衷‘便捷的调用VBA 创造了可能’。当然也遇到一些问题。
问题1:老是被EXCEL 的加载项禁用,需要手动解除。不然就尴尬了
问题2:步骤6,图片出不来,忘记设置本地资源属性设置造成的。
五、实现C# 的VSTO调用VBA函数(xlam)
C#调用VBA(xlam)的代码我会放文末。不管作用大小,好歹是个思路。主要代码如下。
步骤1:添加一个测试项目 test2,做界面就是group里加个button,然后生成XML
步骤2:我的测试界面生成的XML
<?xml version="1.0" encoding="UTF-8"?>
<customUI onLoad="Ribbon_Load" xmlns="http://schemas.microsoft.com/office/2006/01/customui" loadImage="LoadImage">
<ribbon>
<tabs>
<tab id="tab1" label="mytest2">
<group id="group1" label="group1">
<button id="button1" label="button1" size="large" image="exlLogo.jpg" onAction="testdo1" />
</group>
</tab>
</tabs>
</ribbon>
</customUI>
XML 中加入LoadImage、testdo1 ,LoadImage加载图片、testdo1是我想调用的xlam函数。
步骤3: 项目中排除界面文件Ribbon1.cs,
ThisAddIn.cs 中添加 启用XML生成
private Ribbon customerRibbon;
protected override Office.IRibbonExtensibility CreateRibbonExtensibilityObject()
{
customerRibbon = new Ribbon();
return customerRibbon;
}
步骤4:Ribbon.cs写入 本地图片加载
public Image LoadImage(string imageName)
{
Assembly assembly = Assembly.GetExecutingAssembly();
//String[] all = assembly.GetManifestResourceNames();//GetResourceName
//return null;
Stream stream = assembly.GetManifestResourceStream("test2.Resources." + imageName);
return Image.FromStream(stream);
}
步骤5:建一个测试VBA 函数 并保存为 ‘我的测试.xlam’
不需要添加到excel加载项,因为这样外接程序也读取不到,我测试过了。
步骤6:修改按钮事件响应
将Ribbon.cs 的 GetCustomUI 用下面代码覆盖掉
Microsoft.Office.Interop.Excel.Application excelApp = null;
public string GetCustomUI(string ribbonID)
{
excelApp = Globals.ThisAddIn.Application;
// 加载xlam文件(若未自动加载)
excelApp.Workbooks.Open(@"C:\Users\cs\Desktop\我的测试.xlam", ReadOnly: true);
return GetResourceText("test2.Ribbon.xml");
}
public void testdo1(Office.IRibbonControl control)
{
excelApp.Run("我的测试.xlam!模块1.testdo1");
/*
// 方式2:传递参数调用
object arg1 = "参数1";
object arg2 = 100;
excelApp.Run("MyMacro.xlam!Module1.ProcessData", arg1, arg2);
// 方式3:使用动态类型(需引用Microsoft.CSharp)
dynamic excel = excelApp;
excel.Run("MyMacro.xlam!Module1.AdvancedCalc", 15.5, DateTime.Now);
*/
/* try
{
if (control.Id.Equals("btntest"))
{
}
}
catch (COMException ex)
{
Console.WriteLine("Create CTP encounter errors: " + ex.ToString());
}
*/
}
运行效果:
六、结语
1、最终,贴一下本文的测试代码下载
C# 的VSTO调用VBA函数(xlam)示例代码
https://download.csdn.net/download/yqsy123123/90724022
我也不知道直接更新覆盖VBA的文件夹、和写一个自动更新的程序哪个更复杂,主要是想到了看能不能实现,还是挺有意思的
2、至于,写好的VSTO如何注入EXCEL,那就得参考示例2 的安装部分了。
创建 VSTO 外接程序的windows安装包
https://blog.csdn.net/weixin_45661908/article/details/123310069
3、标记一个EXCEL内置图标的,
兼容Office和WPS中Word图标库
https://blog.csdn.net/weixin_45055317/article/details/133832128
你们有可以预览的图标库地址,分享一个,万分感谢。
再次感谢本文引用资源的作者。