【问题标题】:How (or is it possible) to call VBA (sub or function) from ribbon added to Excel through VSTO如何(或可能)从通过 VSTO 添加到 Excel 的功能区调用 VBA(子或函数)
【发布时间】:2020-05-20 20:42:48
【问题描述】:

我正在开发一个项目,其中有一个 Visual Studio C# Excel 插件项目 (VSTO)。从 VSTO 代码中,我将带有按钮的功能区添加到 Excel (Office.IRibbonExtensibility)。 通过 Excel 中添加的按钮,我可以对 C# 代码(在 VSTO 插件中)中的方法进行回调调用。 我正在寻找(而不是寻找)从该按钮调用 VBA(函数的子)到 Excel 文件中的 VBA 代码的方法。 换句话说,通过该按钮,我知道如何调用 C# 中的代码,但不知道如何调用 Excel 文件本身中的 VBA 代码。 我花了相当长的时间搜索信息并测试一些盲目的想法,但没有成功找到任何东西或从我的测试中获得一些好的结果。 我希望能朝着正确的方向迈出第一步。

不确定这是否是稍后(2/10/20 @17:08 GMT -7)添加此注释的正确位置和方法(在获得以下答案后)。我创建了一个小型演示项目并将其上传到 github。项目中也有一个视频(mp4)文件来展示它是如何工作的。 https://github.com/MNemteanu/ExcelVSTOAddInDemo

【问题讨论】:

  • 你不能调用C#然后调用VBA?也许你可以多解释一下你的用例。
  • 我想过这个问题,但不知道它是否会起作用以及如何起作用。那将是一个 B 计划。这将是一种解决方法(字面意思),因为按钮和 VBA 都在 Excel 文件中,并且直接从按钮调用 VBA 而不是去按钮 -> C# - 更有意义> VBA。我只是希望(仍然)找到一个直接呼叫解决方案。如果可以解释用例,它会变得很麻烦,但如果提出任何具体问题,我很乐意添加说明。
  • 感谢蒂姆的链接,我看到并尝试了,但不起作用。如果我不得不依赖这个 12 年前的信息(我猜没有人负责清理 Internet),我应该开始制定计划 B。这家伙说回调必须是 COM 插件不是 VBA:social.msdn.microsoft.com/Forums/vstudio/en-US/…
  • 目的是在卸载 VSTO 加载项后添加功能区并使其持续存在(并能够触发宏)吗?顺便说一句,12 年在 excel 领域并不长……

标签: c# excel vba vsto add-in


【解决方案1】:

这是可能的。
您必须注意,VBA 代码存储在特定的工作簿中,而 VSTO 加载项是在应用程序中加载的,尽管有活动的工作簿。除非开发人员知道,否则两者都不知道对方。
为了实现这种交互,您必须了解以下内容:
1. 宏保存工作簿名称;
2. 宏名。

了解这一点后,您将能够应用在 3d 评论中发布的解决方案。
下面是一个例子。
先决条件:
1. 我准备了启用宏的工作簿“VBA.xlsm”;
2. 本工作簿在常规模块中有一个名为“Foo”的宏。

实施:
1.新建VSTO插件;
2. 添加名为“Ribbon1”的功能区(可视化设计器)并将其设置为具有“自定义”ControlIdType(成为名为“测试”的单独选项卡);
3. 在该功能区中添加一个名为“callVBA”的按钮,该按钮将检查书名并尝试运行工作簿的宏。

我没有向 ThisAddIn.cs 类添加任何代码。我使用的唯一代码 - 在 Ribbon 类中的按钮单击事件处理程序中:

public partial class Ribbon1
    {
        private void Ribbon1_Load(object sender, RibbonUIEventArgs e)
        {

        }

        private void callVBA_Click(object sender, RibbonControlEventArgs e)
        {
            if (Globals.ThisAddIn.Application.ActiveWorkbook.Name == "VBA.xlsm")
            {
                Globals.ThisAddIn.Application.Workbooks["VBA.xlsm"].Application.Run("Foo");
            }
        }
    }

这很容易,但需要您准备好先决条件。
工作原理:

更新 1

这是一种更复杂的方法,它检查打开的工作簿并根据是否存在具有所需宏的工作簿来启用/禁用特定按钮。它还会检查新打开的工作簿并处理仅包含一个 Workbook_Activate 事件的最近关闭的工作簿。
如果您不进行任何检查 - 您可能会收到带有

System.Runtime.InteropServices.COMException

Message=无法将焦点移至控件,因为它不可见、未启用或属于不接受焦点的类型。

 public partial class Ribbon1
    {
        private bool vbaMacroFound;

        private void Ribbon1_Load(object sender, RibbonUIEventArgs e)
        {
            CheckButtons();
            Globals.ThisAddIn.Application.WorkbookActivate += new Excel.AppEvents_WorkbookActivateEventHandler(Workbook_Activate);
        }

        private void callVBA_Click(object sender, RibbonControlEventArgs e)
        {
            if (vbaMacroFound)
            {
                Globals.ThisAddIn.Application.Workbooks["VBA.xlsm"].Application.Run("Foo");
            }
        }

        private void Workbook_Activate(Excel.Workbook Wb)
        {
            CheckButtons();
        }

        private void CheckButtons()
        {
            vbaMacroFound = false;
            this.callVBA.Enabled = false;
            this.callVBA.ScreenTip = "There is no specified macro in none of active workbooks";

            foreach (Excel.Workbook book in Globals.ThisAddIn.Application.Workbooks)
            {

                if (book.Name.Equals("VBA.xlsm"))
                {
                    this.callVBA.Enabled = true;
                    this.callVBA.ScreenTip = "Call the sub from VBA";
                    vbaMacroFound = true;
                }

            }
        }
    }

【讨论】:

  • 非常感谢@Vitaliy 花时间准备这么详细的答案。这真的很重要,尤其是当像我这样的人没有足够的经验来提供不太详细的答案时。
  • 也感谢@TimWilliams 的回答,很抱歉在没有更多帮助的情况下无法跟进您的提示。
  • 嗨@VitaliyPrushak 和蒂姆威廉姆斯只是一个快速的仅供参考,请参阅我最初问题底部的后期说明。不知道我应该把那个笔记放在哪里给其他可能搜索相同信息的人。再次感谢并期待您的更多帮助。
猜你喜欢
  • 1970-01-01
  • 2011-03-27
  • 1970-01-01
  • 2022-07-30
  • 2018-08-29
  • 1970-01-01
  • 1970-01-01
  • 2018-08-29
  • 1970-01-01
相关资源
最近更新 更多