【问题标题】:How to fill-up cells within a Excel worksheet from a VBA function?如何从 VBA 函数填充 Excel 工作表中的单元格?
【发布时间】:2010-10-25 05:47:23
【问题描述】:

我只是想通过 VBA 函数填充电子表格中的单元格。例如,我想在一个单元格中键入 =FillHere(),结果我将有几个单元格填充一些数据。

我试过这样的功能:

Function FillHere()
  Dim rngCaller As Range
  Set rngCaller = Application.Caller
  rngCaller.Cells(1, 1) = "HELLO"
  rngCaller.Cells(1, 2) = "WORLD"
End Function

只要我尝试修改范围,它就会中断。然后我尝试了这个(即使它不是我正在寻找的行为):

Function FillHere()
    Dim rngCaller As Range
    Cells(1, 1) = "HELLO"
    Cells(1, 2) = "WORLD"
End Function

这也不起作用。 但如果我使用 F5 从 VBA 启动此函数,它会起作用! 似乎在调用函数时无法修改电子表格上的任何内容...不过有些库会这样做...

我还尝试(实际上这是我的第一个想法)从函数返回一个数组。问题是我只得到数组中的第一个元素(有一个技巧意味着用左上角的公式选择整个区域+ F2 + CTRL-SHIFT-ENTER,但这意味着用户需要知道通过提前数组的大小)。

我真的被这个问题困住了。我不是最终的最终用户,所以我需要一些非常容易使用的东西,最好不要争论。

PS:很抱歉我已经问过这个问题了,但我当时没有注册,看来我不能再参与其他线程了。

【问题讨论】:

  • 您当前的方法假定始终水平选择至少两个单元格。例如,如果垂直选择它们,则该功能将失败。请问你实际上想用这个函数做什么?此信息可能会有所帮助。
  • 此函数将返回“未知”大小(行和列)的数据。例如,它可能是一个 SQL 请求,又是一个数据库中的表。它将返回列的名称 + 关联的行。
  • 那么基本上,从用户当前所在的单元格开始,你想向下和向左填充一些数据?
  • 没错!就像这些库中的任何一个提供从任何类型的来源获取数据的函数一样。
  • 只要把它写成一个普通的VBA函数,通过Tools > Macros调用,然后使用Application.ActiveCell来决定把结果放在哪里

标签: arrays excel vba function formula


【解决方案1】:

您需要分两步完成:

将您的模块更改为:

Dim lastCall As Variant
Dim lastOutput() As Variant

Function FillHere()
    Dim outputArray() As Variant
    ReDim outputArray(1 To 1, 1 To 2)
    outputArray(1, 1) = "HELLO"
    outputArray(1, 2) = "WORLD"

    lastOutput = outputArray
    Set lastCall = Application.Caller

    FillHere = outputArray(1, 1)
End Function

Public Sub WriteBack()
    If IsEmpty(lastCall) Then Exit Sub
    If lastCall Is Nothing Then Exit Sub

    For i = 1 To UBound(lastOutput, 1)
        For j = 1 To UBound(lastOutput, 2)
            If (i <> 1 Or j <> 1) Then
                lastCall.Cells(i, j).Value = lastOutput(i, j)
            End If
        Next
    Next

    Set lastCall = Nothing
End Sub

然后为了调用 Sub 进入 VBA 中的 ThisWorkbook 区域并添加如下内容:

Private Sub Workbook_SheetCalculate(ByVal Sh As Object)
    Call WriteBack
End Sub

它的作用是返回左上角单元格的值,然后在计算完成后填充其余部分。我写这个的方式假设一次只调用一个 FillHere 函数。如果您想同时重新计算多个变量,那么您将需要一组更复杂的全局变量。

一个警告是,当它填充其他单元格时,它不会关心它覆盖的内容。

编辑: 如果您想在 XLA 中的应用程序范围内执行此操作。 ThisWorkbook 区域的代码应类似于:

Private WithEvents App As Application

Private Sub App_SheetCalculate(ByVal Sh As Object)
    Call WriteBack
End Sub

Private Sub Workbook_Open()
    Set App = Application
End Sub

这将连接应用程序级别的计算。

【讨论】:

  • 有趣的解决方案,并且使用字典来跟踪多个公式可能很简洁。但我的问题还是一样:我不能要求用户在其电子表格的 VBA 中添加这样的代码。如果我可以直接从模块处理 SheetCalculate 事件就完美了……但那是不可能的 :(
  • 您的代码在 XLA 插件中吗?已编辑响应以具有可在应用程序上运行的方法。仍然为一次调用 1 个而构建。
  • 我不知道我们会这样做,这很有趣。但是这样它会覆盖 Worksheet 的 SheetCalculate 方法吗?
  • VBA 进入 XLA 并在应用程序完成任何工作表的计算后被调用。这不会覆盖 WorkSheet_SheetCalculate,而是在 WorkSheet 之后调用。您所做的一项轻微改进是检查回调范围是否实际上在计算的工作表中,因此您只需根据需要进行操作。这与我过去的做法类似,但我的事件处理程序托管在 C# COM 插件中,效果很好。
【解决方案2】:

您尝试执行的操作在 Excel 中不起作用 - 这是设计使然。

不过,您可以这样做:

Function FillHere()
    Redim outputArray(1 To 1, 1 To 2)
    outputArray(1, 1) = "HELLO"
    outputArray(1, 2) = "WORLD"
    FillHere = outputArray
End Function

如果您随后在工作表中选择两个相邻的单元格,输入 =FillHere() 并按 Control+Shift+Enter(应用为数组公式),您应该会看到所需的输出。

【讨论】:

  • 我知道我可以做到这一点,但问题是我们无法提前知道返回数组的长度。因此,在找到完美的范围大小之前,我不能要求最终用户执行测试。不过我确信有办法,因为有些库(尤其是市场准入库)可以很好地完成这项任务。
【解决方案3】:

从根本上说,函数只能影响调用它的单元格。听起来您可能需要考虑使用Worksheet_Change or Worksheet_SelectionChange events 来触发对预期范围内的单元格的修改。

【讨论】:

  • 最终用户会将我的模块插入到他的电子表格中,我不能要求他在任何地方添加 VBA 代码。也许有一种方法可以从函数中注册 SelectionChange 事件,但我对此表示怀疑:(
【解决方案4】:

您可以使用 2 阶段流程间接执行此操作: 编写您的 UDF,以便它以足够持久的方式存储数据(例如全局数组)。 然后有一个插件,其中包含在每个计算事件后触发的应用程序事件,查看 UDF 存储的任何数据,然后重写必要的单元格(如果适当,带有关于覆盖的警告消息)并重置存储的数据。

这样用户就不需要在他们的工作簿中有任何代码。

我认为(但不确定)这是 Bloomberg 等使用的技术。

【讨论】:

  • 什么是足够持久的方式?在 UDF 中,我只能使用静态......但其他任何东西都无法访问它。或者是否可以在 VBA 中有一个 Singleton 类?我会尽力。 Bloomberg 使用 COM 库,似乎您可以使用 Excel COM 对象做任何您想做的事情,除了 VBA。所以这个函数真的可以做任何事情(最重要的是,实时更新)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-29
  • 2023-03-27
  • 2013-07-12
  • 2012-03-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多