简体   繁体   中英

How (or is it possible) to call VBA (sub or function) from ribbon added to Excel through VSTO

I am working on a project where I have a Visual Studio C# Excel Add-in project (VSTO). From the VSTO code I am adding a Ribbon with a button to Excel (Office.IRibbonExtensibility). From the so added button in Excel I can make callback calls to methods in the C# code (in the VSTO add-in). I am looking (and not finding) for the way to call VBA (sub of function) from that button to VBA code that is in the Excel file. In other words, from that described button I know how to call code that is in C# but don't know how to call VBA code that is in the Excel file itself. I spent quite some time searching for information and testing some blind ideas but no success either finding anything or getting some good results from my tests. I would appreciate a kick start in the right direction.

Not sure if this the right place and way to add this later (2/10/20 @17:08 GMT -7) note (after getting the answers below). I have created a small demo project and uploaded it on github. There is a video (mp4) file in the project too to show how it works. https://github.com/MNemteanu/ExcelVSTOAddInDemo

This is possible.
You have to note that VBA code is stored in particular workbook whilst VSTO add-in is loaded within application, despite of the active workbook. And none of both knows about each other, unless developer does.
In order to implement such interaction you have to know following:
1. Macro holding workbook's name;
2. Macro name.

Knowing this you will be able to apply solution posted in 3d comment.
Below is an example.
Prerequisites:
1. I have prepared macro-enabled workbook "VBA.xlsm";
2. This workbook has a macro called "Foo" in regular module.

Implementation:
1. Create new VSTO add-in;
2. Add Ribbon (Visual Designer) called "Ribbon1" and setup it to have "Custom" ControlIdType (to be a separate tab called "Test");
3. Add a button named "callVBA" to that ribbon which will check the name of the book and try to run workbook's macro.

I didn't add any code to ThisAddIn.cs class. The only code I used - is in the button click event handler in the Ribbon class:

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");
            }
        }
    }

That's pretty easy, but requires that you have prepared prerequisites.
How it works:
在此处输入图片说明

Update 1

Here is more complex approach which checks open workbooks and enables/disables specific button based on whether there is a workbook with needed macro. It also checks newly opened and handles recently closed workbooks with only one Workbook_Activate event.
If you don't do any check - you may get a System.Runtime.InteropServices.COMException with a

Message=Can't move focus to the control because it is invisible, not enabled, or of a type that does not accept the focus.

 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;
                }

            }
        }
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM