使Excelfunction影响“其他”单元格

假设我创build了一个Sub(而不是一个函数),它的任务是取出活动单元格(即Selection)并将相邻单元格设置为某个值。 这工作正常。

当您尝试将该Sub转换为一个函数,并尝试从电子表格(即将其公式设置为“= MyFunction()”)评估它的Excel将咆哮的事实,你试图影响的非活动单元格,只需强制该函数返回#VALUE而不触及相邻的单元格。

是否有可能closures这种保护行为? 如果不是的话,有什么方法可以解决这个问题? 如果可能的话,我正在寻找一个有能力的开发人员可以完成1-2周的工作。

问候,艾伦。

注意:我使用的是2002版本,所以我希望可以使用适用于该版本的解决scheme。 话虽如此,如果将来的版本更容易,我也想知道。

这是不可能的,这是有道理的,因为:

  • 当调用工作表函数时,包含函数的单元格不一定是活动单元格。 所以你无法可靠地find相邻的小区。

  • 当Excel重新计算工作表时,它需要维护单元格之间的依赖关系。 所以它不能允许工作表函数任意修改其他单元格。

你可以做的最好的是:

  • 处理SheetChange事件。 如果包含您的function的单元格正在更改,请修改相邻的单元格。

  • 将工作表函数放在相邻的单元格中以返回所需的值。

更新

关于评论:“我想这个函数在一个'空白'的电子表格上工作,所以我不能真正依赖电子表格的SelectionChange事件可能还不存在,但将需要调用此函数”:

  • 你可以把你的函数在一个XLA加载项? 那么您的XLA加载项可以处理在Excel实例中打开的所有工作簿的应用程序SheetChange(*)事件?

关于评论:“但是,如果你将Excel保存在CalculationMode = xlManual并且只填写了值,那么你应该没问题”

  • 即使CalculationMode是xlManual,Excel也需要维护单元格之间的引用依赖关系树,以便它可以按正确的顺序进行计算。 如果其中一个函数可以更新一个任意的单元格,这将会搞乱顺序。 这大概是为什么Excel强加这个限制。

(*)我最初编写了上面的SelectionChange,现在修正了 – 当然,正确的事件是SheetChange的工作簿或应用程序对象,或更改的工作表对象。

更新2在AlanR的文章中描述了如何使用定时器来“工作”

  • 定时器function(“Woohoo”)如何知道要更新哪个单元尚不清楚。 您没有指示哪个单元格包含触发计时器的公式的信息。

  • 如果公式存在于多个单元格中(在相同或不同的工作簿中),那么在重新计算过程中将重复调用UDF,覆盖timerId。 因此,您将无法可靠地销毁计时器,并泄漏Windows资源。

根据如何创build自定义用户定义的Excel函数 :

UDF的限制

  • 除了包含公式的单元格(或范围)以外的单元格中不能放置值。 换句话说,UDF的意思是用作“公式”,不一定是“macros”。

所以,看起来不可能完成。

我正在使用Excel 2007,它不起作用。 Excel提到它创build了一个循环引用。 我不认为你可以改变函数的其他单元格,只是返回一个值。

这是一种函数式编程,没有副作用。 如果你可以改变函数中的其他单元格(从工作表中使用),那么Excel就没有办法知道顺序以及单元格更改时要重新计算的内容。

本文还包含有关Excel如何重新计算的大量信息。 但它从来没有说其他单元格被冻结。

我不知道你在做什么,但是,为什么不把另一个函数放在相邻的单元格中,把第一个单元格作为参数呢?

例:

Public Function Bar(r As Range) As Integer If r.Value = 2 Then Bar = 0 Else Bar = 128 End If End Function 

谢谢大家的回应。 这是可能的! 均田。 我说“有点”,因为从技术上讲,“function”并不影响周围的细胞。 但实际上,没有一个用户可以分辨出来。

诀窍是使用Win32 API来启动一个定时器,一旦它closures,你就可以按你想要的对任意单元格进行操作并closures定时器。

现在我不是COM线程工作方式的专家(尽pipe我知道VBA是Single Apartment Threaded),但是要注意你的Timer在你的Excel进程中跑掉并崩溃。 这实际上不是我build议作为其他电子表格的解决scheme。

用这些内容创build一个模块:

 Option Explicit Declare Function SetTimer Lib "user32" (ByVal HWnd As Long, _ ByVal IDEvent As Long, ByVal mSec As Long, _ ByVal CallFunc As Long) As Long Declare Function KillTimer Lib "user32" (ByVal HWnd As Long, _ ByVal timerId As Long) As Long Private timerId As Long Private wb As Workbook Private rangeName As String Private blnFinished As Boolean Public Sub RunTimer() timerId = SetTimer(0, 0, 10, AddressOf Woohoo) End Sub Public Sub Woohoo() Dim i As Integer ' For i = 0 To ThisWorkbook.Names.Count - 1 ' ThisWorkbook.Names(i).Delete ' Next ThisWorkbook.Worksheets("Sheet1").Range("D8").Value = "Woohoo" KillTimer 0, timerId End Sub 

虽然你不能在Excel中做到这一点,但是在Resolver One中是可能的(尽pipe这仍然是一件非常奇怪的事情)。

这是一个电子表格,允许您在Py​​thon中定义自定义函数,然后可以从网格中的单元格公式中调用。

举个例子,你可能想要定义一个safeDivide函数(而不是引发一个ZeroDivisionError ),通过为分母单元格着色并在其旁边添加错误消息来告诉你这个问题。 你可以像这样定义它:

 def safeDivide(numerator, cellRange): if not isinstance(cellRange, CellRange): raise ValueError('denominator must be a cell range') denominator = cellRange.Value if denominator == 0: cell = cellRange.TopLeft cell.BackColor = Color.Red cell.Offset(1, 0).Value = 'Tried to divide by zero' return 0 return numerator / denominator 

还有一个额外的皱纹:传递单元格的函数刚刚通过了单元格的值,所以为了解决这个问题,我们坚持要为分母传递一个单元格单元格。

如果您尝试使用不适合Excel的电子表格来处理不寻常的事情,或者您有兴趣使用Python的强大function来处理电子表格数据,那么值得看看Resolver One。

这是一个简单的VBA解决方法。 对于此示例,打开一个新的Excel工作簿并将下面的代码复制到Sheet1的代码区域(而不是ThisWorkbook或VBA Module )。 然后进入Sheet1 ,并将东西放到工作表的左上angular单元格中。 如果你input一个数字然后回车,那么右边的单元格将被更新为数字的4倍,单元格背景将变成浅蓝色。 任何其他值导致下一个单元格被清除。 代码如下:

 Dim busy As Boolean Private Sub Worksheet_Change(ByVal Target As Range) If busy Then Exit Sub busy = True If Target.Row <= 10 And Target.Column <= 10 Then With Target.Offset(0, 1) If IsNumeric(Target) Then .Value = Target * 4 .Interior.Color = RGB(212, 212, 255) Else .Value = Empty .Interior.ColorIndex = xlColorIndexNone End If End With End If busy = False End Sub 

子例程捕获表单中的所有单元格更改事件。 如果行和列都是<= 10,则如果值是数字,则右侧的单元格被设置为已更改的单元格的4倍; 否则清除右侧的单元格。