如何将两个 excel vba 代码合并为一个捕获动态变化值的代码

Posted

技术标签:

【中文标题】如何将两个 excel vba 代码合并为一个捕获动态变化值的代码【英文标题】:How can I merge two excel vba code into one which captures dynamically changing values 【发布时间】:2022-01-23 09:44:12 【问题描述】:

我有以下两个几乎相似的 excel vba 代码,但我想将它们合并为一个: 代码 1:

Option Explicit

Private Sub Worksheet_Change(ByVal Target As Range)
   
    On Error GoTo ClearError
    
    Const sfCellAddress As String = "A2" ' source
    Const lCol As String = "B" ' lookup
    Const dCol As String = "C" ' destination
    Const Criteria As String = "CENTER"
    
    Dim sfCell As Range: Set sfCell = Range(sfCellAddress)
    Dim srg As Range: Set srg = sfCell.Resize(Rows.Count - sfCell.Row + 1)
    
    Dim sirg As Range: Set sirg = Intersect(srg, Target)
    
    If sirg Is Nothing Then Exit Sub
        
    ' Relevant Ranges (lcol, dcol)
    Dim lrg As Range: Set lrg = Intersect(sirg.EntireRow, Columns(lCol))
    Dim drg As Range: Set drg = Intersect(sirg.EntireRow, Columns(dCol))
    
    Dim cLen As Long: cLen = Len(Criteria)
    
    Dim lString As String
    Dim dString As String
    Dim r As Long
    
    Application.EnableEvents = False
    
    For r = 1 To lrg.Cells.Count
        lString = CStr(lrg.Cells(r).Value)
        If Len(lString) > 0 Then
            dString = CStr(drg.Cells(r).Value)
            If StrComp(Right(dString, cLen), Criteria, vbTextCompare) <> 0 Then
                If Len(dString) = 0 Then
                    dString = lString
                Else
                    dString = dString & "," & lString
                End If
                drg.Cells(r).Value = dString
            End If
        End If
    Next r
    
SafeExit:
    If Not Application.EnableEvents Then
        Application.EnableEvents = True
    End If
    
    Exit Sub
ClearError:
    Debug.Print "Run-time error '" & Err.Number & "': " & Err.Description
    Resume SafeExit
End Sub

代码 2

Option Explicit

Private Sub Worksheet_Change(ByVal Target As Range)
   
    On Error GoTo ClearError
    
    Const sfCellAddress As String = "A2" ' source
    Const lCol As String = "D" ' lookup
    Const dCol As String = "E" ' destination
    Const Criteria As String = "SURFACE"
    
    Dim sfCell As Range: Set sfCell = Range(sfCellAddress)
    Dim srg As Range: Set srg = sfCell.Resize(Rows.Count - sfCell.Row + 1)
    
    Dim sirg As Range: Set sirg = Intersect(srg, Target)
    
    If sirg Is Nothing Then Exit Sub
        
    ' Relevant Ranges (lcol, dcol)
    Dim lrg As Range: Set lrg = Intersect(sirg.EntireRow, Columns(lCol))
    Dim drg As Range: Set drg = Intersect(sirg.EntireRow, Columns(dCol))
    
    Dim cLen As Long: cLen = Len(Criteria)
    
    Dim lString As String
    Dim dString As String
    Dim r As Long
    
    Application.EnableEvents = False
    
    For r = 1 To lrg.Cells.Count
        lString = CStr(lrg.Cells(r).Value)
        If Len(lString) > 0 Then
            dString = CStr(drg.Cells(r).Value)
            If StrComp(Right(dString, cLen), Criteria, vbTextCompare) <> 0 Then
                If Len(dString) = 0 Then
                    dString = lString
                Else
                    dString = dString & "," & lString
                End If
                drg.Cells(r).Value = dString
            End If
        End If
    Next r
    
SafeExit:
    If Not Application.EnableEvents Then
        Application.EnableEvents = True
    End If
    
    Exit Sub
ClearError:
    Debug.Print "Run-time error '" & Err.Number & "': " & Err.Description
    Resume SafeExit
End Sub

【问题讨论】:

Right(dString, cLen) 看起来不对。 这是this question的后续。 【参考方案1】:

据我所知,您希望采用合理通用的代码并使其可重用。

试试这个。

在 VBA 编辑器中创建一个新模块并粘贴此代码。您在每个工作表上的代码略有变化。我添加了 Target 参数并直接引用了已更改的工作表...

Public Sub OnSheetChange(ByVal Target As Range, ByVal sfCellAddress As String, ByVal lCol As String, _
        ByVal dCol As String, ByVal Criteria As String)
        
    On Error GoTo ClearError
    
    Dim objSheet As Worksheet
    Set objSheet = Target.Worksheet
    
    Dim sfCell As Range: Set sfCell = objSheet.Range(sfCellAddress)
    Dim srg As Range: Set srg = sfCell.Resize(objSheet.Rows.Count - sfCell.Row + 1)
    
    Dim sirg As Range: Set sirg = Intersect(srg, Target)
    
    If Not sirg Is Nothing Then
        ' Relevant Ranges (lcol, dcol)
        Dim lrg As Range: Set lrg = Intersect(sirg.EntireRow, objSheet.Columns(lCol))
        Dim drg As Range: Set drg = Intersect(sirg.EntireRow, objSheet.Columns(dCol))
        
        Dim cLen As Long: cLen = Len(Criteria)
        
        Dim lString As String
        Dim dString As String
        Dim r As Long
        
        Application.EnableEvents = False
        
        For r = 1 To lrg.Cells.Count
            lString = CStr(lrg.Cells(r).Value)
            If Len(lString) > 0 Then
                dString = CStr(drg.Cells(r).Value)
                If StrComp(Right(dString, cLen), Criteria, vbTextCompare) <> 0 Then
                    If Len(dString) = 0 Then
                        dString = lString
                    Else
                        dString = dString & "," & lString
                    End If
                    drg.Cells(r).Value = dString
                End If
            End If
        Next r
    End If
    
SafeExit:
    If Not Application.EnableEvents Then
        Application.EnableEvents = True
    End If
    
    Exit Sub
ClearError:
    Debug.Print "Run-time error '" & Err.Number & "': " & Err.Description
    Resume SafeExit
    
End Sub

...现在从 Worksheet_OnChange 事件方法中,执行类似的操作...

Private Sub Worksheet_Change(ByVal Target As Range)
    OnSheetChange Target, "A2", "B", "C", "CENTER"
    OnSheetChange Target, "A2", "D", "E", "SURFACE"
End Sub

...这将使您的代码可重用。当然,您需要确保它完全适合您,但这是一般的想法。

【讨论】:

感谢您的回复。没有两个 worksheet_change 事件。我希望 vba 在同一张纸上工作。第一个代码适用于 B 列和 C 列,而第二个代码适用于同一张表的 D 列和 E 列。 @Kuldip.Das,我已经修改了代码以满足您的意见。不过,您需要仔细考虑,我的原始答案中有足够的信息来完成这项工作,您只需要稍微修改一下即可。无论哪种方式,看看现在是否适合你。【参考方案2】:

合并相似的Worksheet_Change 代码

说明

对于在 A 列中手动更改的每个单元格(输入、复制/粘贴或 VBA 写入)(不包括单元格 A1)... ...在查找列列表中每一列的同一行(lColsList - B)... ...它将尝试在关联的条件列表(CriteriaList - CENTER;BOTTOM)中查找值(B)。 如果找到值 (B): 如果值 (B / CENTER;BOTTOM) 已经在相关目标列 (dColsList - C) 的单元格中,它将什么也不做。单元格已“密封”。 如果不是,则值 (B) 将附加到单元格 (C) 由于先前的条件“密封”单元格。 如果找不到值 (B): 如果条件列表 (CENTER;BOTTOM) 中已有值,则它不会执行任何操作,因为单元格已“密封”。 如果不是: 如果值 (B) 已经在目标单元格 (C) 中,它将什么都不做。 如果没有,值 (B) 将附加到单元格 (C)。

守则

调整常量部分中的值。 您可能想要删除 ;BOTTOM,因为它的目的只是为了说明您可以在每列中设置更多标准来“密封”(“冻结”)单元格。
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Calls:        Worksheet_Change
'                   DelimitOnChange
'                       DelimitOnChangeWrite
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Option Explicit

Private Sub Worksheet_Change(ByVal Target As Range)
    DelimitOnChange Target
End Sub

Private Sub DelimitOnChange( _
        ByVal Target As Range)

    Const ProcName As String = "DelimitOnChange"
    On Error GoTo ClearError
    
    Const sfCellAddress As String = "A2" ' source
    Const lColsList As String = "B,D" ' lookup
    Const dColsList As String = "C,E" ' destination
    Const CriteriaList As String = "CENTER;BOTTOM,SURFACE"
    Const ListDelimiter As String = "," ' 3 lists (see right above)
    Const CriteriaDelimiter As String = ";" ' multiple criteria per column
    Const ValuesDelimiter As String = "," ' values in lookup column
    
    Dim srg As Range
    With Target.Worksheet
        Dim sfCell As Range: Set sfCell = .Range(sfCellAddress)
        Set srg = sfCell.Resize(.Rows.Count - sfCell.Row + 1)
    End With
    
    Dim sirg As Range: Set sirg = Intersect(srg, Target)
    If sirg Is Nothing Then Exit Sub
        
    Dim lCols() As String: lCols = Split(lColsList, ListDelimiter)
    Dim dCols() As String: dCols = Split(dColsList, ListDelimiter)
    Dim Criteria() As String: Criteria = Split(CriteriaList, ListDelimiter)
        
    Application.EnableEvents = False
    
    Dim n As Long
    For n = 0 To UBound(lCols)
        DelimitOnChangeWrite sirg, lCols(n), dCols(n), Criteria(n), _
            CriteriaDelimiter, ValuesDelimiter
    Next n
                
SafeExit:
    If Not Application.EnableEvents Then
        Application.EnableEvents = True
    End If
    
    Exit Sub
ClearError:
    Debug.Print "'" & ProcName & "': Unexpected Error!" & vbLf _
              & "    " & "Run-time error '" & Err.Number & "':" & vbLf _
              & "    " & Err.Description
    Resume SafeExit
End Sub

Private Sub DelimitOnChangeWrite( _
        ByVal sirg As Range, _
        ByVal lCol As String, _
        ByVal dCol As String, _
        ByVal CriteriaList As String, _
        Optional ByVal CriteriaDelimiter As String = ";", _
        Optional ByVal ValuesDelimiter As String = ",")
    Const ProcName As String = "DelimitOnChangeWrite"
    On Error GoTo ClearError

    Dim Criteria() As String: Criteria = Split(CriteriaList, CriteriaDelimiter)
    Dim cUpper As Long: cUpper = UBound(Criteria)

    Dim lrg As Range: Set lrg = Intersect(sirg.EntireRow, Columns(lCol))
    Dim drg As Range: Set drg = Intersect(sirg.EntireRow, Columns(dCol))
    
    Dim lString As String
    Dim dString As String
    Dim c As Long
    Dim cIndex As Variant
    Dim r As Long
    
    For r = 1 To lrg.Cells.Count
        lString = CStr(lrg.Cells(r).Value)
        If Len(lString) > 0 Then
            dString = CStr(drg.Cells(r).Value)
            If Len(dString) = 0 Then
                dString = lString
            Else
                For c = 0 To cUpper
                    If StrComp(Right(dString, Len(Criteria(c))), _
                            Criteria(c), vbTextCompare) = 0 Then Exit For
                Next c
                If c > cUpper Then
                    If InStr(1, dString, lString, vbTextCompare) = 0 Then
                        dString = dString & ValuesDelimiter & lString
                    End If
                End If
            End If
            drg.Cells(r).Value = dString
        End If
    Next r
                
ProcExit:
    Exit Sub
ClearError:
    Debug.Print "'" & ProcName & "': Unexpected Error!" & vbLf _
              & "    " & "Run-time error '" & Err.Number & "':" & vbLf _
              & "    " & Err.Description
    Resume ProcExit
End Sub

【讨论】:

以上是关于如何将两个 excel vba 代码合并为一个捕获动态变化值的代码的主要内容,如果未能解决你的问题,请参考以下文章

excel或者vba,怎样将工作簿内所有橙色单元格公式转换为数值?

PDF-XChange Editor SDK 将多个 PDF 与 VBA (Excel) 合并

VBA 如何批量将单元格复制到另一个工作表中

Perl如何将两个或多个excel文件合并为一个(多个工作表)?

从 C# 运行 Excel 宏:从 VBA 捕获运行时错误

excel如何用vba批量提取指定工作表?