这个问题可能对许多VBA程序员有用。它涉及实施两个有用的独立任务,并使它们同时工作。
第一项任务是为UDF制作Excel功能工具提示。虽然似乎尚未找到确定的解决方案,但现在我对自定义插入函数向导的解决方案感到满意。您可以在此处找到有关自定义“插入函数向导”的方法实现的主题:How to put a tooltip on a user-defined function 当我说插入函数向导时,我指的是这个窗口:
如果您对寻求实现功能工具提示的最终解决方案的主题感兴趣,可以访问:The quest for the Excel custom function tooltip
第二个任务是在一个不同的单元格中进行UDF写入。我在这里找到了一个很好的解决方案:I don't want my Excel Add-In to return an array (instead I need a UDF to change other cells)
现在,出现了问题:在尝试同时执行这两个任务时Excel崩溃了。我想在调用单个函数时使这两个任务起作用,防止excel崩溃。我想为UDF使用自定义的插入函数向导,该向导可以写入调用它的不同单元格。我想要这样做的原因是我正在编写一个带有多个输入参数的函数的AddIn(因此用户需要输入参数的工具提示)并且我需要在不同于它们被调用的单元格的单元格中写入(因为我不想坚持使用宏。我想要一个功能驱动的AddIn而不是一个按钮驱动的AddIn。)对于熟悉Bloomberg Excel API的人来说,这几乎就是BDH()所做的功能。
我写了两个模块作为问题的指导原则。第一个构建一个displayParameters()函数,需要在运行main函数之前运行。它通过函数驱动的方式执行自定义插入函数向导的任务。第二个函数是名为sumTwoNumbers的main函数,它执行两个数字的和,并将结果显示在与调用函数的单元格不同的单元格中。当您尝试使用插入函数向导(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, i.e., 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, i.e., 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
到目前为止的进展
我发现插入函数向导在每次填充输入参数后在后台运行函数,并在向导中看到的'='后输出结果。因此,如果在使用参数数量不足的情况下运行函数时触发错误,则在向导中提供输入后也会触发该错误。如果使用该数量的输入运行功能时显示消息框,则向导前面将显示一个消息框。但是,当您使用修改的UDF以便将输出写入调用它的不同单元格时,并且在向导填充输入时不会触发错误,excel将会中断。我猜这是因为你的函数在后台运行,触发AfterUDFRoutine1_sumTwoNumbers(),然后触发AfterUDFRoutine2_sumTwoNumbers()。当AfterUDFRoutine2_sumTwoNumbers()最终尝试在excel电子表格中写入并且向导打开时,excel会中断,因为您无法在向导打开的情况下写入单元格。这个问题的一个显而易见的解决方案是找到一种方法,使得插入函数向导在提供每个输入参数后停止在后台运行该函数,并使其等待,直到您单击“确定”后运行该函数。
答案 0 :(得分:0)
上述问题不必要地长。我可以通过询问如何使用&#34;插入函数向导&#34;来概括它。在能够修改其他单元格的UDF中。我坚持使用UDF的概念,可以修改其他单元格,因为我知道它并没有在框外思考。可以执行此操作的UDF的代码(如此处所述:I don't want my Excel Add-In to return an array (instead I need a UDF to change other cells))在用户尝试使用&#34;插入函数向导&#34;时中断Excel。解决此问题的方法是,创建一个自动调整数组输出大小的UDF,而不是创建修改外部单元格的UDF。我找到了这个加载项:https://colinlegg.wordpress.com/2014/08/25/self-extending-udfs-part-1/,它提供了一个名为&#34; = rxlRESIZE()&#34;的函数的实现。调整数组输出的大小。如果在代码中编写该函数,在返回之前立即应用于数组输出,那么只要输出是数组,就可以在其他单元格中写入UDF。最后,当您使用&#34;插入函数向导&#34;时,这可以防止Excel崩溃。为了自定义插入函数向导,我仍然使用从这篇文章中获得的实现:How to put a tooltip on a user-defined function