结合如何自定义UDF的插入函数向导并使UDF操作其他单元的任务

这个问题可能对许多VBA程序员有用。 它涉及执行两个有用的独立任务,并使他们同时工作。

第一个任务是为UDF制作一个Excel函数工具提示。 虽然目前还没有find明确的解决scheme,但现在我对自定义插入function向导的解决scheme感到满意。 你可以在这里find关于实现插入函数向导的方法的主题: 如何把一个工具提示放在一个用户定义的函数上当我说插入函数向导时,我的意思是这个窗口:

插入功能向导

如果您也对寻求实现函数工具提示的权威解决scheme的主题感兴趣,可以到这里: 寻求Excel自定义函数工具提示

第二个任务是使UDF写入一个与之不同的单元格中。 我在这里发现了一个很好的解决scheme: 我不希望我的Excel加载项返回一个数组(而是我需要一个UDF来更改其他单元格)

现在,问题出现了: 试图同时执行这两个任务时,Excel崩溃。 我想在调用一个函数时使这两个任务工作,防止Excel崩溃 。 我想为UDF使用一个自定义的插入函数向导,它可以在不同的单元格中写入。 我为什么要这样做的原因是,我正在编写一个AddIn函数,它需要几个input参数(所以用户需要input参数的工具提示),而且我需要写入与它们被调用的单元格不同的单元格(因为我不想坚持macros,我想要一个function驱动的AddIn,而不是一个button驱动的)。 对于那些熟悉彭博Excel API的人来说,这几乎是BDH()所做的function。

我写了两个模块作为问题的指导。 第一个构build一个displayParameters()函数,需要在运行main函数之前运行。 它通过函数驱动的方式执行自定义插入函数向导的任务。 第二个函数是称为sumTwoNumbers的主函数,它执行两个数字的和,并将结果显示在与函数被调用的单元格不同的单元格中。 当您尝试使用插入函数向导(ctr + A)运行第二个函数(sumTwoNumbers())后,它被自定义(运行displayParameters()后),Excel将崩溃。

模块1:

Option Explicit Private Declare Function SetTimer Lib "user32" ( _ ByVal HWnd As Long, _ ByVal nIDEvent As Long, _ ByVal uElapse As Long, _ ByVal lpTimerFunc As Long _ ) As Long Private Declare Function KillTimer Lib "user32" ( _ ByVal HWnd As Long, _ ByVal nIDEvent As Long _ ) As Long Private mCalculatedCells As Collection Private mWindowsTimerID As Long Private mApplicationTimerTime As Date Public Function displayParameters() As Variant ' This is a UDF that returns true when the volscore file is created and starts a windows timer ' that starts a second Appliction.OnTime timer that performs activities not ' allowed in a UDF. Do not make this UDF volatile, pass any volatile functions ' to it, or pass any cells containing volatile formulas/functions or ' uncontrolled looping will start. displayParameters = "Success" 'Cache the caller's reference so it can be dealt with in a non-UDF routine If mCalculatedCells Is Nothing Then Set mCalculatedCells = New Collection On Error Resume Next mCalculatedCells.Add Application.Caller, Application.Caller.Address On Error GoTo 0 ' Setting/resetting the timer should be the last action taken in the UDF If mWindowsTimerID <> 0 Then KillTimer 0&, mWindowsTimerID mWindowsTimerID = SetTimer(0&, 0&, 1, AddressOf AfterUDFRoutine1_displayParameters) End Function Public Sub AfterUDFRoutine1_displayParameters() ' This is the first of two timer routines. This one is called by the Windows ' timer. Since a Windows timer cannot run code if a cell is being edited or a ' dialog is open this routine schedules a second safe timer using ' Application.OnTime which is ignored in a UDF. ' Stop the Windows timer On Error Resume Next KillTimer 0&, mWindowsTimerID On Error GoTo 0 mWindowsTimerID = 0 ' Cancel any previous OnTime timers If mApplicationTimerTime <> 0 Then On Error Resume Next Application.OnTime mApplicationTimerTime, "AfterUDFRoutine2_displayParameters", , False On Error GoTo 0 End If ' Schedule timer mApplicationTimerTime = Now Application.OnTime mApplicationTimerTime, "AfterUDFRoutine2_displayParameters" End Sub Public Sub AfterUDFRoutine2_displayParameters() ' This is the second of two timer routines. Because this timer routine is ' triggered by Application.OnTime it is safe, ie, Excel will not allow the ' timer to fire unless the environment is safe (no open model dialogs or cell ' being edited). Dim sumTwoNumbersArgumentsDescription(1 To 2) As String sumTwoNumbersArgumentsDescription(1) = "Write the first number of a Sum" sumTwoNumbersArgumentsDescription(2) = "Write the second number of a Sum" Application.MacroOptions Macro:="sumTwoNumbers", _ ArgumentDescriptions:=sumTwoNumbersArgumentsDescription 'Description:="describre the ivol function" MsgBox ("The formal parameters and instance of actual parameters are now successfully displayed at the Insert Function Dialog Box.") End Sub 

模块2:

 Option Explicit 'This global variable is the way I found of passing the output of sumTwoNumbers into the 'function "AfterUDFRoutine2" Dim outputGlobal As Variant Private Declare Function SetTimer Lib "user32" ( _ ByVal HWnd As Long, _ ByVal nIDEvent As Long, _ ByVal uElapse As Long, _ ByVal lpTimerFunc As Long _ ) As Long Private Declare Function KillTimer Lib "user32" ( _ ByVal HWnd As Long, _ ByVal nIDEvent As Long _ ) As Long Private mCalculatedCells As Collection Private mWindowsTimerID As Long Private mApplicationTimerTime As Date 'Non-Volatile Function2 Public Function sumTwoNumbers(Optional ByVal param1 As Integer _ , Optional ByVal param2 As Integer _ ) As Variant sumTwoNumbers = param1 + param2 outputGlobal = sumTwoNumbers sumTwoNumbers = "Success" 'Cache the caller's reference so it can be dealt with in a non-UDF routine If mCalculatedCells Is Nothing Then Set mCalculatedCells = New Collection On Error Resume Next mCalculatedCells.Add Application.Caller, Application.Caller.Address On Error GoTo 0 ' Setting/resetting the timer should be the last action taken in the UDF If mWindowsTimerID <> 0 Then KillTimer 0&, mWindowsTimerID mWindowsTimerID = SetTimer(0&, 0&, 1, AddressOf AfterUDFRoutine1_sumTwoNumbers) End Function Public Sub AfterUDFRoutine1_sumTwoNumbers() ' This is the first of two timer routines. This one is called by the Windows ' timer. Since a Windows timer cannot run code if a cell is being edited or a ' dialog is open this routine schedules a second safe timer using ' Application.OnTime which is ignored in a UDF. ' Stop the Windows timer On Error Resume Next KillTimer 0&, mWindowsTimerID On Error GoTo 0 mWindowsTimerID = 0 ' Cancel any previous OnTime timers If mApplicationTimerTime <> 0 Then On Error Resume Next Application.OnTime mApplicationTimerTime, "AfterUDFRoutine2_sumTwoNumbers", , False On Error GoTo 0 End If ' Schedule timer mApplicationTimerTime = Now Application.OnTime mApplicationTimerTime, "AfterUDFRoutine2_sumTwoNumbers" End Sub Public Sub AfterUDFRoutine2_sumTwoNumbers() ' This is the second of two timer routines. Because this timer routine is ' triggered by Application.OnTime it is safe, ie, Excel will not allow the ' timer to fire unless the environment is safe (no open model dialogs or cell ' being edited). 'Write the output to the sheet Dim dest Set dest = ActiveCell.Offset(0, 1) dest.Value = outputGlobal Set outputGlobal = Nothing End Sub 

迄今取得进展

我发现在每次填充input参数之后,插入函数向导会在后台运行你的函数,并在向导中看到'='后输出结果。 因此,如果在运行function的参数数量不足时触发错误,则在向导中提供input后也会触发该错误。 如果使用该数量的input运行function时显示消息框,则向导将显示一个消息框。 但是,如果使用修改的UDF将输出写入到与其调用的不同单元中,并且在向导填写input时不会触发错误,则Excel将会中断。 我的猜测是,这是因为你的函数在后台运行,触发AfterUDFRoutine1_sumTwoNumbers(),然后触发AfterUDFRoutine2_sumTwoNumbers()。 当AfterUDFRoutine2_sumTwoNumbers()最终尝试在Excel电子表格中写入并且向导打开时,Excel打破了,因为您无法在向导打开的情况下写入单元格。 这个问题的一个明显的解决scheme是find一种方法,使插入函数向导在提供每个input参数后,停止在后台运行该函数,并在点击“确定”后等​​待直到运行该函数。

上面的问题是不必要的长。 我可以通过询问如何在能够修改其他单元的UDF中使用“插入函数向导”来总结它。 我坚持UDF的概念,可以修改其他单元格,因为我知道它,并没有在箱子外面思考。 一个UDF的代码,可以做到这一点,如下所述: 我不希望我的Excel加载项返回一个数组(而是我需要一个UDF来更改其他单元格) ,当用户试图使用“插入function向导“。 解决这个问题的一个解决方法是,不是创build一个修改外部单元格的UDF,而是创build一个自动调整数组输出的UDF。 我find了这个插件: https ://colinlegg.wordpress.com/2014/08/25/self-extending-udfs-part-1/,它给出了一个名为“= rxlRESIZE()”的函数的实现,你的数组输出。 如果在代码中写入该函数,在返回之前立即应用到数组输出,那么只要输出是一个数组,就可以在其他单元格中写入UDF。 最后,这可以防止Excel在使用“插入函数向导”时崩溃。 对于自定义插入函数向导,我仍然使用从这篇文章中得到的实现: 如何把一个工具提示放在一个用户定义的函数