22

我要做的是使用 VSTO 的 C#“Excel 2007 插件”项目类型为 Excel 创建用户定义函数 (UDF)(因为我只想生成一些通用 UDF)。因为我只是想学习基础知识(无论如何在这个阶段),这就是我的代码的样子:

using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Linq;
using Excel = Microsoft.Office.Interop.Excel;
using Office = Microsoft.Office.Core;
using Microsoft.Office.Tools.Excel;
using Microsoft.Office.Tools.Excel.Extensions;
using System.Runtime.InteropServices;

namespace ExcelAddIn1
{
    public partial class ThisAddIn
    {
        private void ThisAddIn_Startup(object sender, System.EventArgs e)
        {}

        private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
        {}

        //My UDF
        public static double HeronicCal(int a, int b, int c)
        {
            //first compute S = (a+b+c)/2
            double S = (a + b + c) / 2;    
            double area = Math.Sqrt(S * (S - a) * (S - b) * (S - c));
            return area;
        }

        #region VSTO 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.Startup += new System.EventHandler(ThisAddIn_Startup);
            this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
        }            
        #endregion
    }
}

它编译得很好,当我运行它时,Excel 会弹出一个新的电子表格,当我查看“加载项”列表(在 Excel 选项中)时,我可以在列表中看到我的加载项(设置到“启动时加载)。但是我的问题来了,当我尝试从 Excel 中调用我的 UDF 时,Excel 找不到该方法!

我想错了,我必须将我的方法标记为 Excel UDF(使用方括号 - 例如在编写 webservices -> “[WebService]”时所做的)。但是我一直没能找到这个标签(因为我根本不确定我的预感是否正确),这就是为什么我决定去找你们这里的好人。

所以我的问题基本上是——从我的代码那里,有什么简单的方法可以让 Excel 访问我的 UDF?如果是,如何?

我真的很想留在 VSTO 项目类型(加载项、工作簿、模板)中,因为我当前项目的总体目标是确定使用 VS2010/Excel2007 执行 C# UDF 是否以可接受的速度工作。为了测试这一点,我正在使用 Windows7RC 和 VS2010 beta1。

4

4 回答 4

22

VSTO 不支持创建 Excel UDF。可以在 .Net 中创建自动化加载项,并且似乎是 Microsoft 认可的执行方式。

你应该看看 ExcelDna - http://www.codeplex.com/exceldna。ExcelDna 允许托管程序集通过本机 .xll 接口向 Excel 公开用户定义的函数 (UDF) 和宏。该项目是开源的,可以免费用于商业用途。您会发现基于 .Net 的 UDF 的性能类似于 Excel 的原生 .xll 插件。支持 Excel 2007 的大型工作表、长 Unicode 字符串和多线程重新计算等功能。

使用 ExcelDna,您上面发布的函数将在没有 VSTO 的情况下暴露给 Excel - 您可以将代码放入基于 xml 的 .dna 文件或将其编译为 .dll。

暴露 UDF 的 .dna 文件如下所示:

<DnaLibrary Language="C#">
   using System;
   using ExcelDna.Integration;

   public class MyFunctions
   {
      [ExcelFunction(Description="Calculate Stuff", Category="Cool Functions")]
      public static double HeronicCal(int a, int b, int c)
      {
         //first compute S = (a+b+c)/2
         double S = (a + b + c) / 2;
         double area = Math.Sqrt(S * (S - a) * (S - b) * (S - c));
         return area;        
      }
   }
</DnaLibrary>

更新:如今,开始使用 Excel-DNA 的最简单方法是在 Visual Studio 中创建一个新的类库项目,然后从 NuGet 添加“ExcelDna.AddIn”包。这就是一个入门插件 - 只需粘贴您的代码并按 F5 即可运行。

于 2009-06-12T12:38:25.200 回答
13

看起来埃里克卡特在这里有一个赢家:

https://docs.microsoft.com/en-us/archive/blogs/eric_carter/3-vsto-bug-tracker-a-udf

它是纯 .NET - 不依赖第三方库。

现在给它一个树瘤...

于 2011-06-09T00:30:14.383 回答
9

据我所知,您不能直接在 VSTO 中创建 UDF。

请参阅 Paul Stubbs 的文章如何在 VSTO 托管代码中创建 Excel UDF,其中他使用 VBA 加载项来公开 VBA UDF,这反过来又调用了他用 VSTO 编写的托管 UDF。

但是,在不使用 VSTO 时,您可以使用托管代码创建 UDF。请参阅 Eric Carter 的文章在 .NET 中为 Excel 编写用户定义的函数,了解如何执行此操作。

至于 VSTO 的执行速度,我想你会发现它几乎适用于所有任务。然而,循环遍历已经是 Excel 的弱点的单元格可能会非常缓慢,具体取决于您在做什么。尽量批量执行。例如,不是逐个循环遍历单元格,而是从一个区域返回一个二维值数组,处理该数组,然后将其传递回该区域。

为了演示,下面将从一个区域返回一个二维值数组,处理这些值,然后将结果数组一次性传递回原始区域:

Excel.Range rng = myWorksheet.get_Range("A1:D4", Type.Missing);

//Get a 2D Array of values from the range in one shot:
object[,] myArray = (object[,])rng.get_Value(Type.Missing);

// Process 'myArray' however you want here.
// Note that the Array returned from Excel is base 1, not base 0.
// To be safe, use GetLowerBound() and GetUpperBound:
for (int row = myArray.GetLowerBound(0); row <= myArray.GetUpperBound(0); row++)
{
    for (int column = myArray.GetLowerBound(1); column <= myArray.GetUpperBound(1); column++)
    {
        if (myArray[row, column] is double)
        {
            myArray[row, column] = (double)myArray[row, column] * 2;
        }
    }
}

// Pass back the results in one shot:
rng.set_Value(Type.Missing, myArray);

希望这可以帮助!

麦克风

于 2009-06-07T15:59:31.497 回答
0

我发现效果很好的是将 UDF 保留为 VB 模块以避免 COM 对象的问题。

我有大量的 C# 代码正在运行,当我准备好构建版本时,我会执行以下操作:
1. 添加一个模块:
   开发人员 [Excel 中的选项卡] | Visual Basic -> 项目窗口,右键单击,插入模块
   - 只需在此处复制/粘贴 VB 代码
2. 包括适当的参考库(同一 VB 窗口中的工具)
3. 将 Excel 文件另存为 .xlsm(即,宏-启用)

然后,您可以删除 .xlsx 文件。

我所做的是压缩整个目录(例如,“Release”)并将其发送给我们的用户。

于 2013-08-30T13:50:22.503 回答