我正在用C#开发一个COM可见的DLL,并且这个DLL在Excel VBA项目中被引用。这个想法是将所有代码外包给 DLL,而不是使用 VBA。 VBA仅用于订阅我的Excel工作簿的不同事件:
Option Explicit
Private Sub Workbook_Open()
With New ProjectXLib.EndPoint
Call .Subscribe(Excel.ThisWorkbook)
End With
End Sub
这是我的
IEndPoint
界面:
using System;
using System.Runtime.InteropServices;
using Excel = Microsoft.Office.Interop.Excel;
namespace ProjectXLib.Source
{
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("6ca9d5e5-41fe-45f3-8ccb-8106288a447c")]
public interface IEndPoint
{
void Subscribe(Excel.Workbook workbook);
}
}
这是
IEndPoint
的实现:
using ExcelCoreLib.Source.Core;
using ProjectXLib.Source.Events;
using System;
using System.Runtime.InteropServices;
using Excel = Microsoft.Office.Interop.Excel;
namespace ProjectXLib.Source
{
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[Guid("e8b3885e-8ea3-4bcb-baa2-b5b7b1e9f8f3")]
[ProgId(nameof(EndPoint))]
public class EndPoint : IEndPoint
{
public EndPoint() { }
public void Subscribe(Excel.Workbook workbook)
{
using (Macro.GetInstance(workbook.Application))
{
WorkbookEvents.Subscribe(workbook);
}
}
}
}
我在
WorkbookEvents
类中订阅我的工作簿事件和 ActiveX 控件以进行编码:
using ExcelCoreLib.Source.Core;
using System.Windows.Forms;
using Excel = Microsoft.Office.Interop.Excel;
using MSForms = Microsoft.Vbe.Interop.Forms;
namespace ProjectXLib.Source.Events
{
internal static class WorkbookEvents
{
private static Excel.Workbook Workbook { get; set; }
private static Excel.WorkbookEvents_Event Events { get; set; }
private static MSForms.CommandButton BtnErrors { get; set; }
public static void Subscribe(Excel.Workbook workbook)
{
Workbook = workbook;
Events = workbook;
// Buttons
BtnErrors = Worksheet.GetCommandButton(workbook.Worksheets["Errors"], "btnErrors");
BtnErrors.Click += BtnErrors_Click;
// Events
Events.BeforeClose += Workbook_BeforeClose;
Events.SheetBeforeDoubleClick += Workbook_SheetBeforeDoubleClick;
Events.SheetChange += Workbook_SheetChange;
Events.AfterSave += Workbook_AfterSave;
}
private static void BtnErrors_Click()
{
MessageBox.Show("Hello world from dll!");
}
private static void Workbook_BeforeClose(ref bool Cancel)
{
Events.BeforeClose -= Workbook_BeforeClose;
Events.SheetBeforeDoubleClick -= Workbook_SheetBeforeDoubleClick;
Events.SheetChange -= Workbook_SheetChange;
Events.AfterSave -= Workbook_AfterSave;
BtnErrors.Click -= BtnErrors_Click;
}
private static void Workbook_SheetBeforeDoubleClick(object Sh, Excel.Range Target, ref bool Cancel)
{
if (Target.Count.Equals(1) && Target.Value2 is bool && !Target.HasFormula)
{
Cancel = true; Target.Value2 = !Target.Value2;
}
}
private static void Workbook_SheetChange(object Sh, Excel.Range Target)
{
Worksheet.CastAs(Sh).CircleInvalid();
}
private static void Workbook_AfterSave(bool Success)
{
Excel.Worksheet worksheet = Workbook.ActiveSheet;
worksheet.CircleInvalid();
}
}
}
所有这些都运行良好。我使用 Regasm 注册我的 DLL,在 Excel 工作簿中引用它,事件和按钮事件工作正常。我的问题是我无法从 CustomRibbonUI 订阅按钮:
<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui">
<ribbon startFromScratch="false">
<tabs>
<tab id="idMacros" label="Macros">
<group id="idErrors" label="Errors">
<button id="btnReset" label="Reset" imageMso="HappyFace" size="large"/>
<button id="btnFindInvalidCells" label="Invalid cells" imageMso="HappyFace" size="large"/>
</group>
</tab>
</tabs>
</ribbon>
</customUI>
在给你写信之前我查了很多资料。理想情况下,我不想在 VBA 中进行任何额外的实现,除了加载功能区时的
RibbonOnLoad
方法之外,就像我在打开工作簿时使用 Workbook_Open
事件订阅工作簿事件一样。例如,我想将我的 btnReset
和 btnFindInvalidCells
按钮订阅与 Click 相关的事件,并在 DLL 中完成这些事件的实现。
除此之外,我尝试添加和实现 IRibbonExtensibility 接口但没有成功,也许我做错了。
感谢那些读到这里的人和那些帮助我的人。
为什么您甚至想订阅特定的按钮按下事件? (您选择这样做的原因是什么?)因为据我了解,这会将 DLL 中的实现与自定义功能区实现结合起来。
<button id="btnFindInvalidCells" [...] onAction="handleInvalidCells"/>
Sub handleInvlaidCells(control As IRibbonControl)
'call DLL function here
End Sub
这样,功能区菜单和 DLL 中的代码就可以独立更改,而无需更改其他