如何将C#类公开给文档级外接程序中的VBA模块?

这是一个假设的情况。

我想知道是否可以在文档级外接程序中将C#类暴露给VBA。

这是一个SSCCE:

在VS PRO 2012中,我启动了一个新项目, 选定办公室 – > Excel 2010工作簿 。 ( 确保选择.Net framework ver 4

我已向Sheet1添加了DateTimePicker控件。

我可以在C#解决方案中的DateTimePicker控件中设置/获取.Value属性而不会出现问题。

在调试时:在VBA中, 公开.Value属性。 ( 试过.OLEFormat.Object.Value

在此处输入图像描述

并非所有属性都可以向VBA公开,因为ActiveX控件DateTimePickerMSForms包装,因此Excel可识别它(兼容性)。

我需要能够从VBA获取包装控件的实际值,但我不确定如何去做( 是否可能 )…

我知道控件本身支持事件,但这不是我想要的路径。 我希望能够从控件中获取静态/当前值。

这是我希望能够做到的:

  • 在我的C#解决方案中添加一个类

  • 公开它,所以它可以从像Dim obj as new MyExposedClass这样的VBA Dim obj as new MyExposedClass

  • 然后让MyExposedClass存储对DateTimePicker引用,因为它出现在C#中(所有属性都可用)

  • 然后我可以定义一个函数GetValue(string controlName) ,它返回C#POV中的Value

所以我发现这个解决方案 +( 这个 )似乎适用于应用程序级外接程序,但它不适用于文档级外接程序。

当我调试我的解决方案并打开VBA的对象浏览器时,我可以看到引用被自动添加到Microsoft Visual Studio 2008 Tools for Office Execution Engine 9.0 Type Library但我认为我不能为它添加额外的类…

当我在VBE中打开引用时,没有额外的引用添加到项目中,但在我的解决方案的/ debug文件夹中有一个ExcelWorkbook1.dll那么它是如何附加到解决方案的呢?

所以我的问题是:

如何使用C#在Excel的文档级外接程序中公开类,以扩展.Net控件上默认可访问的属性范围?

更新:

这是我到目前为止最接近但它只允许你公开主题项,如工作表,工作簿,图表等。它允许你调用方法,所以我将进一步调查,并回来一些反馈

从VBA调用文档级自定义中的代码

如何:在Visual C#项目中将代码暴露给VBA

演练:在Visual C#项目中从VBA调用代码

您需要创建一个公共接口以将该类公开给VBA,这对我来说是一个文档级别的插件。

  1. 打开一个新的Excel工作簿并将以下内容复制到MODULE中

     Sub CallVSTOMethod() Dim dt As Date Dim VSTOSheet1 As DocLevelAddin.Sheet1 Set VSTOSheet1 = GetManagedClass(Sheet1) dt = VSTOSheet1.GetDatePickerVal End Sub 
  2. 将Excel保存为“TestProj.xlsm”并关闭。

  3. 打开VS,新项目,Excel 20xx工作簿并将项目命名为“DocLevelAddin”
  4. 在向导中,选择复制现有文档并选择新创建的工作簿“TestProj.xlsm”
  5. 在Excel Sheet1上将DateTimePicker控件添加到来自VS的工作表,双击以创建ValueChanged事件并更新Sheet1.cs中的代码以读取

     private DateTime dtVal; private void dateTimePicker1_ValueChanged(object sender, EventArgs e) { dtVal = dateTimePicker1.Value; } 
  6. 仍然在Sheet1.cs中,添加一个公共方法来返回dtVal

     public DateTime GetDatePickerVal() { return dtVal; } 
  7. 还要将以下内容添加到Sheet1.cs中

     protected override object GetAutomationObject() { return this; } 
  8. 在Sheet1.cs中的公共部分类Sheet1上面添加以下内容

     [System.Runtime.InteropServices.ComVisible(true)] [System.Runtime.InteropServices.ClassInterface( System.Runtime.InteropServices.ClassInterfaceType.None)] 
  9. 现在,您需要为该方法创建一个公共接口。 在Sheet1.cs中右键单击选择Refactor,Extract Interface并检查公共方法GetDatePickerVal

  10. 使界面公开,COM可见

     [System.Runtime.InteropServices.ComVisible(true)] public interface ISheet1 { DateTime GetDatePickerVal(); } 
  11. 双击Sheet1.cs,以便Excel工作表可见。 选择任何单元格以打开属性窗口并更改属性ReferenceAssemblyFromVbaProject = true

  12. 在Excel中,您可能需要转到信任中心设置并将VS解决方案文件夹和子文件夹添加为受信任位置

  13. 运行项目,Excel MODULE中的代码将通过公开的GetDatePickerVal方法返回dateTimepicker。

在此处输入图像描述

Sheet1.cs:

 using System; using System.Collections.Generic; using System.Data; using System.Linq; using System.Text; using System.Windows.Forms; using System.Xml.Linq; using Microsoft.Office.Tools.Excel; using Microsoft.VisualStudio.Tools.Applications.Runtime; using Excel = Microsoft.Office.Interop.Excel; using Office = Microsoft.Office.Core; namespace DocLevelAddin { [System.Runtime.InteropServices.ComVisible(true)] [System.Runtime.InteropServices.ClassInterface( System.Runtime.InteropServices.ClassInterfaceType.None)] public partial class Sheet1 : DocLevelAddin.ISheet1 { private void Sheet1_Startup(object sender, System.EventArgs e) { } private void Sheet1_Shutdown(object sender, System.EventArgs e) { } #region VSTO Designer generated code ///  /// Required method for Designer support - do not modify /// the contents of this method with the code editor. ///  private void InternalStartup() { this.dateTimePicker1.ValueChanged += new System.EventHandler(this.dateTimePicker1_ValueChanged); this.Startup += new System.EventHandler(this.Sheet1_Startup); this.Shutdown += new System.EventHandler(this.Sheet1_Shutdown); } #endregion private DateTime dtVal; private void dateTimePicker1_ValueChanged(object sender, EventArgs e) { dtVal = dateTimePicker1.Value; } public DateTime GetDatePickerVal() { return dtVal; } protected override object GetAutomationObject() { return this; } } } 

ISheet1.cs:

 using System; namespace DocLevelAddin { [System.Runtime.InteropServices.ComVisible(true)] public interface ISheet1 { DateTime GetDatePickerVal(); } }