Как обрабатывать события из встроенных Excel.OleObjects или Excel.Shapes
Я работаю над портами C#
и теперь VB.NET
старой программы VBA
. В него встроено множество MSForms/OleObjects
, таких как CommandButton
или даже изображения.
Моей первой мыслью было объявить все кнопки как Microsoft.Vbe.Interop.Forms.CommandButton
, но это привело к исключению COM
, что System._COM type
нельзя привести к ...Forms.CommandButton
. Если я попробую более общую версию этого решения, я не найду никаких элементов, и если я попытаюсь пройти все VBComponet
, я отмечу, что все они являются листами в workbook
, но ни один из элементов управления:
foreach (VBComponent xxx in Globals.ThisWorkbook.VBProject.VBComponents) {
Interaction.MsgBox(xxx.Name);
Interaction.MsgBox(xxx.ToString);
}
Таким образом, все эти элементы управления отсутствуют в .VBComponets
, но я могу найти их как OLEobjects в thisworkbook.worksheets(n).OLEobjects
(для меня это противоречит здравому смыслу, но я, вероятно, не понимаю систему для начала с).
Как мне обработать действие Click от такого объекта?
Я предполагаю, что мне нужно использовать интерфейс Excel.OLEObjectEvents_Event
, но я не могу понять, как это сделать. Если я пытаюсь сделать пользовательские события с delegates
, я не могу назначить их на OleObjects
. Если я использую ActionClickEventHandler.CreateDelegate
, я могу получить огромное количество ошибок, которые заставляют меня думать, что это тупик.
официальная документация от MS не кажется такой полезной, хотя она и познакомила меня с идеей из Verb
, которую я изучаю. До сих пор это вызывало только ошибки COM по типу "Приложение не удалось запустить".
Даже просто пытаясь использовать одно из двух стандартных событий, .GotFocus
, я всегда получаю ошибку 0x80040200.
Пример:
Excel.OLEObject ButtonCatcher = Globals.ThisWorkbook.Worksheets(1).OLEObjects("CommandButton1");
ButtonCatcher.GotFocus += CommandButton1_Click;
Создает исключение COMException Exception from HRESULT: 0x80040200
во второй строке. Кнопка включена, что я и проверил после просмотра кода с сайта разработчика Office.
Попытка более универсального подхода в коде для листа, содержащего элементы управления:
object CommandButtonStart = this.GetType().InvokeMember("CommandButton1", System.Reflection.BindingFlags.GetProperty, null, this, null);
Выдает ошибку недостающего метода.
Любая помощь очень ценится, кажется, это должно быть очевидно, и я скучаю по ней.
** Редактировать: я также обнаружил, что я могу привести эти элементы управления в Excel.Shape
, но это на самом деле не приближает меня к запуску функции или подпрограммы из VSTO. Я играю с Excel.Shape.OnAction
, но для этого требуется саб VBA. Предположительно, я мог бы вызвать сабвуфер VBA, который вызывает сабвуфер из VSTO, если VSTO был видимым COM. Это кажется очень сложным, и я бы хотел сделать это только в крайнем случае.
Ответы
Ответ 1
Вы пытались использовать NewLateBinding.LateGet?
using MSForms = Microsoft.Vbe.Interop.Forms;
using Microsoft.VisualBasic.CompilerServices;
...
MSForms.CommandButton CommandButton1 = (MSForms.CommandButton)NewLateBinding.LateGet(Globals.ThisWorkbook.Worksheets(1), null, "CommandButton1", new object[0], null, null, null);
CommandButton1.Click += new Microsoft.Vbe.Interop.Forms.CommandButtonEvents_ClickEventHandler(CommandButton1_Click);
Он ссылается на MSDN на форумах VSTO и в старый блог пост.
Ответ 2
Тип решения: Уровень документа VSTO
Сценарий:
1.) Excel.Решет, созданный во время выполнения. (а не элемент рабочего листа)
2.) Добавить кнопку на Рабочем листе во время выполнения, которая запускает код С# при нажатии.
Ссылки на сборку:
Microsoft.Vbe.Interop(Microsoft.Vbe.Interop.dll)
Microsoft.Vbe.Interop.Forms(Microsoft.Vbe.Interop.Forms.dll)
Microsoft.VisualBasic(Microsoft.VisualBasic.dll)
Протестировано/Рабочий код:
using MSForms = Microsoft.Vbe.Interop.Forms;
using System.Windows.Forms;
...
Microsoft.Vbe.Interop.Forms.CommandButton CmdBtn;
private void CreateOLEButton()
{
Excel.Worksheet ws = Globals.ThisWorkbook.Application.Sheets["MyWorksheet"];
// insert button shape
Excel.Shape cmdButton = ws.Shapes.AddOLEObject("Forms.CommandButton.1", Type.Missing, false, false, Type.Missing, Type.Missing, Type.Missing, 500, 5, 100, 60);
cmdButton.Name = "btnButton";
// bind it and wire it up
CmdBtn = (Microsoft.Vbe.Interop.Forms.CommandButton)Microsoft.VisualBasic.CompilerServices.NewLateBinding.LateGet(ws, null, "btnButton", new object[0], null, null, null);
CmdBtn.Caption = "Click me!";
CmdBtn.Click += new MSForms.CommandButtonEvents_ClickEventHandler(ExecuteCmd_Click);
}
private void ExecuteCmd_Click()
{
MessageBox.Show("Click");
}
Ответ 3
Можете ли вы программно добавить код в CodeModule в Workbook, как это?
Private Sub CommonButton_Click(ByVal buttonName As String)
MsgBox "You clicked button [" & buttonName & "]"
End Sub
Private Sub CreateEventHandler(ByVal buttonName As String)
Dim VBComp As VBIDE.VBComponent
Dim CodeMod As VBIDE.CodeModule
Dim codeText As String
Dim LineNum As Long
Set VBComp = ThisWorkbook.VBProject.VBComponents(Me.CodeName)
Set CodeMod = VBComp.CodeModule
LineNum = CodeMod.CountOfLines + 1
codeText = codeText & "Private Sub " & buttonName & "_Click()" & vbCrLf
codeText = codeText & " Dim buttonName As String" & vbCrLf
codeText = codeText & " buttonName = """ & buttonName & "" & vbCrLf
codeText = codeText & " CommonButton_Click buttonName" & vbCrLf
codeText = codeText & "End Sub"
CodeMod.InsertLines LineNum, codeText
End Sub
Ответ 4
Используйте инструментарий для форм interop. Это бесплатно от Microsoft. Он предоставляет com wrappers и event messenger class, который связывает данные событий с .NET и VBA. Я использовал его для обработки контрольных событий из VBA в .NET и событий от .NET до VBA. Вы должны использовать interop
http://www.codeproject.com/Articles/15690/VB-C-Interop-Form-Toolkit.
Из инструментария ducoumentation:
Interop UserControls предоставляет базовый набор встроенных событий (Click, GotFocus, Validate и т.д.) в Visual Basic 6.0. Вы можете определить свои собственные события в Visual Studio.NET и поднять их с помощью RaiseEvent. Чтобы обрабатывать события в Visual Basic 6.0, вам нужно добавить ссылку на проект и добавить объявление переменной WithEvents в свой код, а затем обработать события, используя переменную WithEvents, а не сам элемент управления.
Как обрабатывать пользовательские события UserControl для пользователей
1.
В Visual Basic 6.0 в меню "Проект" нажмите "Ссылки". Обратите внимание, что уже есть ссылка на ControlNameCtl, где ControlName - имя вашего Interop UserControl.
2.
В списке "Доступные ссылки" найдите ссылку для ControlName и проверьте ее, а затем нажмите "ОК".
3.
В Редакторе кода добавьте объявление для переменной WithEvents:
Dim WithEvents ControlNameEvents As ControlLibrary.ControlName
В обработчике событий Form_Load добавьте следующий код для инициализации переменной WithEvents:
Private Sub Form_Load()
Set ControlNameEvents = Me.ControlNameOnVB6Form
End Sub
Добавить код для обработки вашего настраиваемого события в обработчике событий переменной WithEvents:
Private Sub ControlNameEvents_MyCustomEvent()
MsgBox("My custom event fired")
End Sub