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

时间:2013-12-04 15:44:44

标签: c# excel add-in user-defined-functions excel-addins

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

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

编辑1

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

我有两个问题:

  1. 我无法确定如何在写入函数的单元格中显示消息。例如,如果函数在单元格A1中运行,则会显示消息“正在处理...”,直到写入单元格的值。
  2. 此实现会重新计算函数。
  3. 我已经看到了第1项和第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 个答案:

答案 0 :(得分:1)

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