Как (или это возможно) вызвать VBA (суб или функцию) с ленты, добавленной в Excel через VSTO - PullRequest
0 голосов
/ 05 февраля 2020

Я работаю над проектом, в котором у меня есть проект Visual Studio C# Надстройка Excel (VSTO). Из кода VSTO я добавляю ленту с кнопкой в ​​Excel (Office.IRibbonExtensibility). С помощью добавленной в Excel кнопки я могу выполнять обратные вызовы методов в коде C# (в надстройке VSTO). Я ищу (и не нахожу) способ вызова VBA (подфункции) из этой кнопки в код VBA, который находится в файле Excel. Другими словами, из этой описанной кнопки я знаю, как вызвать код, который находится в C#, но не знаю, как вызвать код VBA, который находится в самом файле Excel. Я потратил довольно много времени на поиск информации и тестирование некоторых слепых идей, но безуспешно ни найти ничего, ни получить хорошие результаты от моих тестов. Я был бы признателен за начало удара в правильном направлении.

Не уверен, что это правильное место и способ добавить это примечание позже (2/10/20 @ 17: 08 GMT -7) (после получения ответов) ниже). Я создал небольшой демонстрационный проект и загрузил его на github. В проекте также есть файл видео (mp4), чтобы показать, как он работает. https://github.com/MNemteanu/ExcelVSTOAddInDemo

1 Ответ

2 голосов
/ 05 февраля 2020

Это возможно.
Следует иметь в виду, что код VBA хранится в конкретной книге, в то время как надстройка VSTO загружается в приложение, несмотря на активную книгу. И никто из них не знает друг о друге, если только разработчик не знает.
Чтобы реализовать такое взаимодействие, вы должны знать следующее:
1. Макрос, содержащий имя книги;
2. Имя макроса.

Зная это, вы сможете применить решение, опубликованное в 3D-комментарии.
Ниже приведен пример.
Предварительные условия:
1. Я подготовил рабочую книгу с поддержкой макросов "VBA.xlsm" ;
2. В этой книге есть макрос под названием «Foo» в обычном модуле.

Реализация:
1. Создайте новую надстройку VSTO;
2. Добавьте ленту (Visual Designer) с именем «Ribbon1» и установите для нее «Custom» ControlIdType (чтобы он был отдельной вкладкой). под названием "Test");
3. Добавьте кнопку с именем "callVBA" на эту ленту, которая проверит название книги и попытается запустить макрос книги.

Я не добавил ни одного кода в класс ThisAddIn.cs. Единственный код, который я использовал, - это обработчик события нажатия кнопки в классе ленты:

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

Это довольно просто, но требует, чтобы вы подготовили предварительные условия.
Как это работает:
enter image description here

Обновление 1

Вот более сложный подход, который проверяет открытые книги и включает / отключает указанную кнопку c в зависимости от того, есть ли рабочая книга с необходимым макросом Он также проверяет вновь открытые и обрабатывает недавно закрытые книги только с одним событием Workbook_Activate.
Если вы не выполните какую-либо проверку - вы можете получить System.Runtime.InteropServices.COMException с

Сообщение = Не удается переместить фокус на элемент управления, потому что он невидим, не включен или имеет тип, который не принимает фокус.

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

            }
        }
    }
...