使Excel函数影响';其他';细胞

使Excel函数影响';其他';细胞,excel,formula,vba,Excel,Formula,Vba,假设我创建了一个子单元(不是函数),它的任务是获取活动单元(即选择),并将相邻单元设置为某个值。这个很好用 当您尝试将该子函数转换为函数并尝试从电子表格对其求值(即将其公式设置为“=MyFunction()”)时,Excel会对您试图影响非活动单元格的值这一事实发出狂吠,并简单地强制该函数返回#值,而不接触相邻单元格 是否可以关闭此保护行为?如果不是的话,有什么好办法可以绕过它?我正在寻找一些有能力的开发人员可以在1-2周的时间内完成,如果可能的话 问候,, 艾伦 注意:我使用的是2002,所以

假设我创建了一个子单元(不是函数),它的任务是获取活动单元(即选择),并将相邻单元设置为某个值。这个很好用

当您尝试将该子函数转换为函数并尝试从电子表格对其求值(即将其公式设置为“=MyFunction()”)时,Excel会对您试图影响非活动单元格的值这一事实发出狂吠,并简单地强制该函数返回#值,而不接触相邻单元格

是否可以关闭此保护行为?如果不是的话,有什么好办法可以绕过它?我正在寻找一些有能力的开发人员可以在1-2周的时间内完成,如果可能的话

问候,, 艾伦


注意:我使用的是2002,所以我倾向于使用适用于该版本的解决方案。话虽如此,如果将来的版本使这一点变得更容易,我也想了解一下。

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

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

还包含许多有关Excel如何重新计算的信息。但它从来没有说其他细胞是冷冻的

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

例如:

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

UDF的局限性

  • 不能将值放置在包含的单元格(或范围)以外的单元格中 公式。换句话说,UDF是 作为“公式”使用,而不是 必须是“宏”

所以,看起来这是不可能的。

这是不可能的,因为:

  • 调用工作表函数时,包含该函数的单元格不一定是活动单元格。因此,您无法可靠地找到相邻单元

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

您所能做的最好的事情是:

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

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

更新


关于评论:“我希望此函数在‘空白’电子表格上工作,因此我不能真正依赖电子表格的SelectionChange事件,该事件可能还不存在,但需要调用此函数”:

    你能把你的函数放在一个XLA插件中吗?那么,XLA插件可以处理Excel中的所有工作簿的应用程序SheetChange(*)事件,即Excel?。
关于评论:“尽管如此,如果您在CalculationMode=xlManual中使用Excel并只填写值,您应该不会有问题”

  • 即使CalculationMode是xlManual,Excel也需要维护单元格之间引用的依赖关系树,以便能够按正确的顺序进行计算。如果其中一个函数可以更新任意单元格,这将打乱顺序。这大概就是Excel施加此限制的原因
(*)我最初在上面写了SelectionChange,现在更正了-当然正确的事件是工作簿或应用程序对象的SheetChange,或者工作表对象的Change

更新2 关于如何使用定时器“使其工作”的一些说明:

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

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


    • 谢谢大家的回复。这是可能的!有点。我之所以说“有点”,是因为从技术上讲,“功能”并不影响周围的细胞。然而,实际上,没有用户能够分辨出两者之间的区别

      诀窍是使用Win32 API启动计时器,一旦计时器关闭,您就可以对任何单元格执行您想要的操作并关闭计时器

      现在我不是COM线程工作原理方面的专家(虽然我知道VBA是单单元线程),但要小心计时器与Excel进程一起跑掉并导致它崩溃。这并不是我建议的其他电子表格的解决方案

      只需使用以下内容制作一个模块:

      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中执行此操作,但也可以在中执行(尽管这仍然是一件非常奇怪的事情)

      它是一个电子表格,允许您用Python定义自定义函数,然后可以从网格中的单元格公式调用这些函数

      例如,您可能希望定义一个
      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的强大功能处理电子表格数据感兴趣,那么值得一看Resolver One。

      以下是
      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