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

10 c# vba vsto add-in office-interop

这是一个假设的情况.

我想知道是否可以在文档级外接程序中将C#类公开给VBA.

这是一个SSCCE:

在VS PRO 2012中,我启动了一个新项目,选择Office - > Excel 2010 Workbook.(确保选择.Net framework ver 4)

我已经DateTimePicker向Sheet1 添加了一个控件.

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

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

在此输入图像描述

并非所有属性都可以暴露给VBA,因为ActiveX控件DateTimePicker被包装,MSForms因此Excel可以识别它(兼容性).

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

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


这是我希望能够做到的:

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

  • 暴露它,所以它可以从VBA中恢复过来 Dim obj as new MyExposedClass

  • 然后在C#中显示MyExposedClass商店参考DateTimePicker(所有可用的属性)

  • 然后我可以定义一个GetValue(string controlName)从C#POV返回值的函数


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

当我调试我的解决方案并打开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调用代码

小智 6

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

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

    Sub CallVSTOMethod()
    Dim dt As Date
    Dim VSTOSheet1 As DocLevelAddin.Sheet1
        Set VSTOSheet1 = GetManagedClass(Sheet1)
        dt = VSTOSheet1.GetDatePickerVal
    End Sub
    
    Run Code Online (Sandbox Code Playgroud)
  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;
    }
    
    Run Code Online (Sandbox Code Playgroud)
  6. 仍然在Sheet1.cs中,添加一个公共方法来返回dtVal

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

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

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

  10. 使界面公开,COM可见

    [System.Runtime.InteropServices.ComVisible(true)]
    public interface ISheet1
    {
        DateTime GetDatePickerVal();
    }
    
    Run Code Online (Sandbox Code Playgroud)
  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

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        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;
        }

    }
}
Run Code Online (Sandbox Code Playgroud)

ISheet1.cs:

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