如何比较 VBA 中的值?

How do I compare values in VBA?

我有这个潜艇。当按下用户表单上的按钮时它会被激活,并用于对一个条目进行计数。我将此数据库中的条目总数存储在 A1 中。还有另一个按钮用于倒数一个条目,它工作得很好。他们都有检查,因此他们不会加载不存在的条目。不知何故这个不起作用。

Private Sub ButtonRight_Click()
    
    MsgBox TextBoxID.Value
    MsgBox Cells(1, 1).Value
    MsgBox (TextBoxID.Value < Cells(1, 1).Value)
    If TextBoxID.Value < Cells(1, 1).Value Then
        LoadEntry (TextBoxID.Value + 1)
    End If

End Sub

LoadEntry Sub 也用在其他地方并且有效。我有这个带有 MsgBox 的输出内容用于调试。它给出输出 1、2、false。所以 (1 < 2) = false.

这里是另一个有效的比较:

Private Sub ButtonLeft_Click()
    
    If TextBoxID.Value > 1 Then
        LoadEntry (TextBoxID.Value - 1)
    End If
    
End Sub

问题是隐式转换。

字符串作为文本进行比较,因此“10”小于“2”,因为它按字母顺序 排序。

Debug.Print "10" > "2" ' output: False

TextBox 控件的值始终是 String;为了将其视为数值,您必须首先将其转换为数值 - 但前提是这样做是合法的(例如,“ABC”没有等效的数值)。

此外,单元格的值是一个 Variant,它可能包含一个数字或另一个可以(将)正确但隐式转换为数值的值,但它也可以是一个 Variant/Error (例如 #N/A#VALUE! 错误)每次您尝试将它与任何东西(除了另一个 Variant/Error 值),因此单元格的值也应该在比较之前进行验证和显式转换:

Dim rawValue As String
rawValue = TextBoxID.Value

If IsNumeric(rawValue) Then
    Dim numValue As Double
    numValue = CDbl(rawValue)

    Dim cellValue As Variant
    cellValue = ActiveSheet.Cells(1, 1).Value

    If IsNumeric(cellValue) Then
        If numValue < CDbl(cellValue) Then
            LoadEntry numValue + 1
        End If
    End If

End If

请注意,不合格的 Cells 隐含地指的是 无论 ActiveSheet 是什么 - 如果这不是意图,请考虑使用显式 Worksheet 对象,例如Sheet1.Cells(1, 1)。如果它 故意的,请考虑使用 ActiveSheet 对其进行限定,以便代码说出它所做的,并按照它说的去做。

比较VBA中不同类型的值不是一个简单的任务,比较的结果取决于变量的类型,转换为数字的可能性等。变体变量的比较方式不同于“非变体”变量。参见 https://docs.microsoft.com/en-us/office/vba/language/reference/user-interface-help/comparison-operators

根据文档,TextBox 对象的值 属性 具有基本类型 Variant(请参阅 https://docs.microsoft.com/en-us/office/vba/language/reference/user-interface-help/value-property-microsoft-forms)。

因此,比较Variant/String(TextBox.Value与字符串)和Variant/Double(Cell.Value与数字)的结果-TextBox.Value总是大于Cell.Value:

Private Sub CommandButton1_Click()
    TextBox1.Value = "123"
    [A1].Value = 9999

    Debug.Print "TextBox1.Value = " & TextBox1.Value & ", Type is " & TypeName(TextBox1.Value)
    Debug.Print "[A1].Value = " & [A1].Value & ", Type is "; TypeName([A1].Value)
    Debug.Print "TextBox1.Value > [A1].Value : (" & TextBox1.Value & " > " & [A1].Value & ") is " & (TextBox1.Value > [A1].Value)

    Me.Hide
End Sub

'Output:  
'TextBox1.Value = 123, Type is String  
'[A1].Value = 9999, Type is Double  
'TextBox1.Value > [A1].Value : (123 > 9999) is True

因此,比较前建议:

  • 将比较值的类型减少为一种;
  • 处理类型转换错误

简单的方法是使用 Val() 函数 https://docs.microsoft.com/en-us/office/vba/language/reference/user-interface-help/val-function

Private Sub ButtonRight_Click()
    If Val(TextBoxID.Value) < Val(Cells(1, 1).Value) Then
        LoadEntry (TextBoxID.Value + 1)
    End If
End Sub

同样出于这个目的,我建议创建一个函数:

Function getNumDef(v As Variant, Optional defV As Long = -1) As Long
    getNumDef = defV    'inintially getNumDef set as defaul value
    On Error Resume Next
    getNumDef = CLng(v) ' if error occurs, getNumDef value remains defV
End Function

可以通过以下方式申请:

Private Sub ButtonRight_Click()
    Dim TBV as Long, CV as Long
    TBV = getNumDef(TextBoxID.Value)    'Type conversion and error handling
    CV = getNumDef(Cells(1, 1).Value)   'Type conversion and error handling
    
    If TBV < 0 Or CV < 0 Then
        MsgBox "Some of the values are not numeric or less than 0" _
            & vbCrLf & "Check the raw data", vbCritical + vbOKOnly, "Sub ButtonRight_Click()"
    Else
        If TBV < CV Then
            'The parentheses in `LoadEntry (TextBoxID.Value + 1)` are syntax sugar,
            ' i.e. the argument `TextBoxID.Value + 1` in parentheses is passed as ByVal.
            'If the argument without (), i.e. `LoadEntry TextBoxID.Value + 1`,
            'it is passed as described in the Sub definition or the default ByRef
            
            LoadEntry TextBoxID.Value + 1
        End If
    End If
End Sub