带参数的Excel自定义函数

问题描述 投票:1回答:2

我正在尝试在excel模块中创建自定义函数,如下所示:

Function STATUS(valuex As String)

    If ActiveCell.Offset(0, 1).Value = valuex Then

    ActiveCell.Value = ActiveCell.Offset(0, -1).Value

    'Remove value from left column
     Activecell.offset(0,1).clearcontents

   End If

End Function

它基本上会这样做:

Number  Result  Status
          11    System
22              Type
          33    System
          44    System
55              Hardware
66              Type
          77    System
          88    System
99              Software
110             Type
         121    System
         132    System
143             Hardware
154             Type
165             Type
         176    System
187             Hardware
198             Type
209             Software

如果right cell = valuex(例如字符串“System”)类似于valuex,则将左单元格值放在公式/函数单元格中并删除左列值。

但无论我编程什么,它返回的都是零(0)或名称#错误。

请帮忙

excel vba function
2个回答
7
投票

您正在编写用户定义函数(UDF),即公共标准模块中的Public Function,可以从工作表单元格调用 - 并且此特定类型的函数具有一组特定的约束。例如,不允许有副作用。 UDF接受一些输入,处理它,然后返回结果。

所以UDF的签名应如下所示:

Public Function {name}({args}) As {type}

在编写函数时,首先要考虑的是你需要它返回什么 - 换句话说,在计算之后,=MYFUNCTION(A1,A2)的单元格应该包含什么。例如,如果你写了一个Add函数,将两个Double值加在一起,你会希望它返回一个Double

Public Function Add(ByVal value1 As Double, ByVal value2 As Double) As Double

函数体由给定参数计算结果:

    Dim result As Double
    result = value1 + value2

在返回/退出之前,您需要分配函数的返回值。这是通过分配函数的标识符来完成的:

    Add = result

Excel的计算引擎然后获取该结果,这就是具有=Add(2, 2)等公式的单元格最终得到的值为4


你的STATUS函数依赖于ActiveCell,它是当前在ActiveSheet上选择的任何单元格:它不是调用该函数的单元格。如果选择任何随机单元格,重新计算工作簿可能会产生损坏的结果。

作为UDF,不允许在单元格上使用.ClearContents(或以任何方式影响任何其他单元格) - 这就是为什么函数为进入条件块的执行路径返回#NAME?错误,并且因为没有返回值是在分配之后,另一个执行路径产生0,这是Empty变体的数字表示,这是你的函数当前返回的。

如果UDF需要知道另一个单元格的值,那么最好的办法就是将该单元格的值作为参数:这种方式可以正常运行,而不需要对工作表的布局做任何假设。如果VLOOKUP没有采用lookup_value论证而是从.Offset(0, 1)细胞中获取该值,那么Public Sub有多大用处?会有骚乱!


当你的要求是做某事而不是计算/计算某事时,你需要的不是UDF,而是宏。

宏是公共标准模块(或Worksheet模块)中的无参数Shape过程,可以从“宏”窗口调用,或者在用户单击CommandButton,ActiveX OnAction时执行,或者可以将它们分配给一些自定义菜单项的Sub属性 - 无论你的船是什么岩石。

Slide程序做某事,他们是行动。他们可以访问和更改全局状态,修改任何单元格,工作表或工作簿;他们甚至可以产生一个PowerPoint实例并将图表作为图片粘贴到新的STATUS上 - 你可能想到的任何东西,天空是极限!

因为你需要的东西是做的东西,你需要编写的代码需要更像宏。不要叫它Sub;使用动词并描述它正在做什么:您根据给定的标准将值从一列移动到另一列。当您编写MoveValues Sheet1.Range("$B$2:$B$22"), "System" 过程时,首先考虑如何调用它。

我觉得这样的事情会很整洁:

Private Sub MoveValues(ByVal Target As Range, ByVal Criteria As String)

签名看起来像这样:

Target

并且身体现在可以遍历指定的Criteria范围,评估右边的单元格是否与With Sheet1 MoveValues .Range("A2:A22"), .Range("B2:B22"), .Range("C2:C22"), "System" End With 匹配,然后相应地将值移动到左侧。或者更好 - 我们根本不假设工作表布局,并像这样调用它:

Private Sub MoveValues(ByVal Source As Range, ByVal Target As Range, ByVal Status As Range, ByVal Criteria As String)

现在,如果我们需要在A和B之间,或者在B和C之间插入一个列,我们只需要更改我们传递给过程的参数,而不是过程本身!

    If Source.Columns.Count <> 1 Or Target.Columns.Count <> 1 Or  Status.Columns.Count <> 1 Or _
       Source.Rows.Count <> Target.Columns.Count Or _
       Status.Rows.Count <> Target.Columns.Count _
    Then
        Err.Raise 5
    End If

但首先,我们需要验证我们的假设,并在未达到预期时决定做什么 - 我们需要具有相同行数的单列范围!

在许多情况下,提出运行时错误是最好的办法。错误#5“无效的过程调用或参数”似乎非常合适:

MoveValues

我们甚至可以自定义错误消息,以帮助我们稍后调试调用代码,当我们在6个月后更改参数并忘记关于 If Source.Columns.Count <> 1 Or Target.Columns.Count <> 1 Or Status.Columns.Count <> 1 Or _ Source.Rows.Count <> Target.Columns.Count Or _ Status.Rows.Count <> Target.Columns.Count _ Then Err.Raise 5, "MoveValues", _ "Source, Target, and Status ranges must be 1 column and the same number of rows." End If 过程的假设的所有内容时:

Criteria

我们还需要验证我们的 If Trim$(Criteria) = vbNullString Then Err.Raise 5, "MoveValues", "Criteria string cannot be empty or whitespace." End If 不是空的,或者只是空白!

Source

现在我们已经验证了我们的输入,其余的过程可以安全地假设TargetStatus Dim current As Long For current = 1 To Target.Rows.Count If Status.Cells(current).Value = Criteria Then Target.Cells(current).Value = Source.Cells(current).Value Source.Cells(current).ClearContents End If Next 范围都是单列范围,它们都是相同的大小,并且我们有一个有效的标准可以使用。所以我们可以继续迭代单元格并做我们的事情:

Public Sub MoveSystemValues()
    With Sheet1
        MoveValues .Range("A2:A22"), .Range("B2:B22"), .Range("C2:C22"), "System"
    End With
End Sub

现在剩下要做的就是编写一个调用它的宏:

MoveSystemValues

现在我们可以从Excel的宏窗口运行MoveSystemValues宏,或者将Shape分配给某些http://excelhints.com/2009/02/12/difference-between-sub-and-function/或按钮...然后意识到它对于少量行很有效,但是在较大范围内相当慢 - 但我们有现在足以咀嚼了。


2
投票

使用UDF(用户定义的函数)无法执行所需的操作。

你是从VBA开始的,所以你需要知道SUBS和FUNCTIONS之间的主要区别。

子程序对您的Excel文件执行操作(选择,清除内容,更改工作表,打开另一个工作簿,进行计算等操作)。

函数返回一个值,但不执行任何操作。把它们想象成你自己的公式Excel。 Excel中的公式不执行操作,它们只是根据某些参数返回值。

更多信息:Public Function DeleteWorkSheet() As Boolean Sheets(3).Delete End Function

因此,您需要将代码重写为子过程,然后执行它:)

更新:我在前几行中所说的并不总是100%真实。我发布了这个,因为对于使用VBA开发代码的新手,我认为这是一个很好的起点。当你是一个新手时,从我的角度来看,我认为只使用subs进行操作并仅使用函数来获得自定义计算更容易学习。然后,通过一些经验,进入下一级并开始组合它们并使用它们来做更复杂的事情。在执行代码时可以组合子和函数。

当您从子系统调用UDF时,它可以执行某些操作(例如删除工作表)。我说了一些行动,因为说实话,我不知道是否所有行动都可用。

但是如果你从一个单元格中调用UDF,将其键入为普通的Excel公式,那么它就不会执行任何操作。

一个例子:

Sub Macro1()
DeleteWorkSheet
End Sub

此UDF将删除我的工作簿的第三个工作表。

要从子系统中调用它,它将是:

qazxswpoi

是的,它会删除第三个工作表。

但是如果我从像普通Excel公式那样的单元格中调用这个UDF,那么它什么都不做。

希望这个澄清有所帮助。

© www.soinside.com 2019 - 2024. All rights reserved.