如何在 Excel 用户定义函数中写入数组

Posted

技术标签:

【中文标题】如何在 Excel 用户定义函数中写入数组【英文标题】:How to write array in Excel User Defined Function 【发布时间】:2013-12-04 15:44:44 【问题描述】:

我有一个使用 Excel-DNA 创建的 Excel 插件,并且有一个 UDF 作为 Excel 插件的一部分。假设函数在单元格 A10 中,我去删除 Z 列。我发现此操作会导致函数再次执行。

有没有办法防止这种行为发生?这与计算模型或波动率有关吗?

编辑 1

我正在使用以下代码来实现类似于 Bloomberg BDH 功能的行为 - 即,该函数写入第一个单元格,而数组的其余部分通过线程写入。

我有两个问题:

    我无法确定如何在写入函数的单元格中显示消息。例如,如果函数在单元格 A1 中运行,则会显示消息“正在处理...”,直到写入单元格的值。 使用此实现会重新计算函数。

我已经看到上面第 1 项和第 2 项运行良好的实现,但无法确定如何执行。有人有什么想法吗

public class ArrayWriter

    #region Methods

    #region WriteArray
    [ExcelFunction(IsHidden=true)]
    public object WriteArray(object[,] arrayToWrite)
    
        object caller = null;
        object formula = null;
        AddInFacade facade;

        // if not in a function
        if (!ExcelDnaUtil.IsInFunctionWizard())
        
            facade = new AddInFacade();

            if (arrayToWrite != null)
            
                // if writing more than one cell, use threads
                if (arrayToWrite.GetLength(0) > 1 || arrayToWrite.GetLength(1) > 1)
                
                    var xlApp = ExcelDnaUtil.Application as Application;
                    Type xlAppType = xlApp.GetType();

                    caller = xlApp.Caller;
                    //caller = xlAppType.InvokeMember("ActiveCell", BindingFlags.GetProperty, null, xlApp, null);
                    formula = xlAppType.InvokeMember("FormulaR1C1Local", BindingFlags.GetProperty, null, caller, null);

                    // create instance of ObjectForThread and set all properties of the class
                    ObjectForThread threadObject = new ObjectForThread()
                    
                        xlRef = caller,
                        value = arrayToWrite,
                    ;

                    // create a new thread calling the method WriteFromThread and start the thread
                    Thread threadWriter = new Thread(() => WriteFromThread(threadObject));
                    threadWriter.Start();
                
                else
                
                    facade.SetMouseCursor(XlMousePointer.xlDefault);
                
            
            else
            
                arrayToWrite = new object[1, 1];
                arrayToWrite[0, 0] = "No data was returned.";
                facade.SetMouseCursor(XlMousePointer.xlDefault);
            
        

        return arrayToWrite[0,0];
    
    #endregion

    #region WriteFromThread
    private void WriteFromThread(Object boxedThreadObject)
    
        AddInFacade facade = new AddInFacade();

        ObjectForThread unboxedThreadObject = (ObjectForThread)boxedThreadObject;
        Object cellBelow;

        Type typeCellReference = unboxedThreadObject.xlRef.GetType();

        try
        
            for (int i = 0; i < unboxedThreadObject.value.GetLength(0); i++)
            
                for (int j = 0; j < unboxedThreadObject.value.GetLength(1); j++)
                
                    // do not write the first cell as this is what is returned by the function
                    if (i > 0 || j > 0)
                    
                        cellBelow = typeCellReference.InvokeMember("Offset", BindingFlags.GetProperty, null, unboxedThreadObject.xlRef, new object[]  i, j );
                        typeCellReference.InvokeMember("Value", BindingFlags.SetProperty, null, cellBelow, new[]  Type.Missing, unboxedThreadObject.value[i, j] );
                    
                
            
        
        catch(Exception ex)
        
            string szError = ex.Message;
        
        finally
        
            // attempt to kill all COM references
            unboxedThreadObject.xlRef = null;
            unboxedThreadObject.value = null;

            //Set the mouse cursor to the default cursor since the entire array has now been written
            facade.SetMouseCursor(XlMousePointer.xlDefault);

            unboxedThreadObject = null;
            cellBelow = null;
            facade = null;
        
    
    #endregion

    #endregion

    #region ObjectForThread Class
    public class ObjectForThread
    
        public object xlRef  get; set; 
        public object[,] value  get; set; 
    
    #endregion

【问题讨论】:

【参考方案1】:

更改此行为的唯一方法是更改​​Application.Calculation 属性。但你不想那样做。此属性只能暂时更改,之后应重置为以前的值。否则,您正在编写的插件在共享沙盒中无法正常运行。

【讨论】:

以上是关于如何在 Excel 用户定义函数中写入数组的主要内容,如果未能解决你的问题,请参考以下文章

在Excel C#范围内写入二维对象数组

在 Excel C# 范围内写入一个二维对象数组

如何在所有excel中使用用户定义的函数而不需要额外的操作?

excel vba如何将选中的不连续单元格赋值到数组

在 Excel 中,如何调用存储在另一个工作簿中的用户定义函数而无需打开另一个工作簿?

如何让 Excel 加载项 (Excel 2007) 中的用户定义函数与自动完成功能一起使用?