Home > Mobile >  How do I use to similar subs in the same sheet?
How do I use to similar subs in the same sheet?

Time:11-03

I can use the two below subs individually and they work fine but I cannot figure out how to use them together on the same sheet. When I do I keep getting errors no matter how I rearrange.

FIRST SUB:

Private Sub Worksheet_Change(ByVal Target As Range)
ActiveSheet.Unprotect Password:="incoming"
Dim WorkRng As Range
Dim Rng As Range
Dim xOffsetColumn As Integer
Set WorkRng = Intersect(Application.ActiveSheet.Range("b:b"), Target)
xOffsetColumn = -1
If Not WorkRng Is Nothing Then
    Application.EnableEvents = False
    For Each Rng In WorkRng
        If Not VBA.IsEmpty(Rng.Value) Then
            Rng.Offset(0, xOffsetColumn).Value = Now
            Rng.Offset(0, xOffsetColumn).NumberFormat = "mm-dd-yyyy"
        Else
            Rng.Offset(0, xOffsetColumn).ClearContents
        End If
        Next
    Application.EnableEvents = True
    ActiveSheet.Protect Password:="incoming"
    End If
End Sub

SECOND SUB:

Private Sub Worksheet_Change(ByVal Target As Range)
ActiveSheet.Unprotect Password:="incoming"
Dim WorkRng As Range
Dim Rng As Range
Dim xOffsetColumn As Integer
Set WorkRng = Intersect(Application.ActiveSheet.Range("g:g"), Target)
xOffsetColumn = 2
If Not WorkRng Is Nothing Then
    Application.EnableEvents = False
    For Each Rng In WorkRng
        If Not VBA.IsEmpty(Rng.Value) Then
            Rng.Offset(0, xOffsetColumn).Value = Now
            Rng.Offset(0, xOffsetColumn).NumberFormat = "mm-dd-yyyy"
        Else
            Rng.Offset(0, xOffsetColumn).ClearContents
        End If
        Next
    Application.EnableEvents = True
    ActiveSheet.Protect Password:="incoming"
    End If
End Sub

CodePudding user response:

Use the Option Explicit on your modules.

You can have only one Worksheet_Change event on a worksheet.

Private Sub Worksheet_Change(ByVal Target As Range)
    ActiveSheet.Unprotect Password:="incoming"

    Dim WorkRng As Range
    Dim xOffsetColumn As Integer

    If Not Intersect(Application.ActiveSheet.Range("b:b"), Target) Is Nothing Then
        Set WorkRng = Intersect(Application.ActiveSheet.Range("b:b"), Target)
        xOffsetColumn = -1
        Call DoYourStuff(WorkRng, xOffsetColumn)
    End If
    
    If Not Intersect(Application.ActiveSheet.Range("g:g"), Target) Is Nothing Then
        Set WorkRng = Intersect(Application.ActiveSheet.Range("g:g"), Target)
        xOffsetColumn = 2
        Call DoYourStuff(WorkRng, xOffsetColumn)
    End If
    
    ActiveSheet.Protect Password:="incoming"
End Sub

Private Sub DoYourStuff(rg As Range, Col As Long)
    Dim Rng As Range
    
    Application.EnableEvents = False
    
    For Each Rng In rg
        If Not VBA.IsEmpty(Rng.Value) Then
            Rng.Offset(0, Col).Value = Now
            Rng.Offset(0, Col).NumberFormat = "mm-dd-yyyy"
        Else
            Rng.Offset(0, Col).ClearContents
        End If
    Next
    
    Application.EnableEvents = True
End Sub

CodePudding user response:

All subs (within a given scope) must have unique names. Hence, you can only have one event handler for any given worksheet event (i.e. only one sub named Worksheet_Change in a given worksheet module). I'm not exactly sure of your intent, but (as a best guess) the following code 're-arrangement' seems like what you're trying to do.

Private Sub Worksheet_Change(ByVal Target As Range)
    Dim xOffsetColumn%, Rng As Range, WorkRng As Range
    
    Application.EnableEvents = False
    ActiveSheet.Unprotect Password:="incoming"
    
    Set WorkRng = Intersect(ActiveSheet.Range("b:b"), Target)
    If Not WorkRng Is Nothing Then
        xOffsetColumn = -1
        
        For Each Rng In WorkRng.Cells
            If Rng <> "" Then
                Rng.Offset(0, xOffsetColumn).Value = Now
                Rng.Offset(0, xOffsetColumn).NumberFormat = "mm-dd-yyyy"
            Else
                Rng.Offset(0, xOffsetColumn).ClearContents
            End If
        Next
    End If

    Set WorkRng = Intersect(ActiveSheet.Range("g:g"), Target)
    If Not WorkRng Is Nothing Then
        xOffsetColumn = 2

        For Each Rng In WorkRng.Cells
            If Rng <> "" Then
                Rng.Offset(0, xOffsetColumn).Value = Now
                Rng.Offset(0, xOffsetColumn).NumberFormat = "mm-dd-yyyy"
            Else
                Rng.Offset(0, xOffsetColumn).ClearContents
            End If
            Next
    End If

    ActiveSheet.Protect Password:="incoming"
    Application.EnableEvents = True
End Sub

You can of course re-factor the above to simplify and/or make your code more elegant. The above though should address your core question.

CodePudding user response:

A Worksheet Change (Multiple Criteria)

Sheet Module e.g. Sheet1

Option Explicit

Private Sub Worksheet_Change(ByVal Target As Range)
    
    Const FirstRow As Long = 2
    Const sColsList As String = "B,G"

    Dim sCols() As String: sCols = Split(sColsList, ",")
    
    Dim irg As Range
    Dim srg As Range
    Dim n As Long
    Dim rOffset As Long 
    
    For n = 0 To UBound(sCols)
        rOffset = FirstRow - 1
        With Me.Columns(sCols(n))
            Set srg = .Resize(.Rows.Count - rOffset).Offset(rOffset)
        End With
        Set irg = Intersect(srg, Target)
        If Not irg Is Nothing Then
            ProcessColumnRange irg, n
        End If
    Next n

End Sub

Standard Module e.g. Module1

Option Explicit

Sub ProcessColumnRange( _
        ByVal SourceRange As Range, _
        ByVal ColumnIndex As Long)
' Needs the 'RefCombinedRange' function.
    
    Const tsNumberFormat As String = "mm-dd-yyyy"
    Const dColsList As String = "A,I"
    Const Pwd As String = "incoming"
    
    Application.ScreenUpdating = False
    Application.EnableEvents = False
    On Error GoTo ClearError
    
    Dim ws As Worksheet: Set ws = SourceRange.Worksheet
    ws.Unprotect Pwd
    
    Dim dCols() As String: dCols = Split(dColsList, ",")
    Dim dCol As String: dCol = dCols(ColumnIndex)
    
    Dim sCell As Range ' Source Cell
    Dim dCell As Range ' Destionation Cell
    Dim dcrg As Range ' Destination Clear Range
    Dim dtsrg As Range ' Destination Time Stamp Range
    
    For Each sCell In SourceRange.Cells
        Set dCell = sCell.EntireRow.Columns(dCol)
        If IsEmpty(sCell) Then
            If Not IsEmpty(dCell) Then
                Set dcrg = RefCombinedRange(dcrg, dCell)
            End If
        Else
            Set dtsrg = RefCombinedRange(dtsrg, dCell)
        End If
    Next sCell
    
    If Not dcrg Is Nothing Then
        dcrg.ClearContents
    End If
    
    If Not dtsrg Is Nothing Then
        Dim TimeStamp As Date: TimeStamp = Now
        dtsrg.Value = TimeStamp
        dtsrg.NumberFormat = tsNumberFormat
    End If
    
    ws.Protect Pwd
    
SafeExit:
    Application.EnableEvents = True
    Application.ScreenUpdating = True
    
    Exit Sub
ClearError:
    Debug.Print "Run-time error '" & Err.Number & "': " & Err.Description
    Resume SafeExit
End Sub

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose:      Creates a reference to a range combined from two ranges.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function RefCombinedRange( _
    ByVal CombinedRange As Range, _
    ByVal AddRange As Range) _
As Range
    If CombinedRange Is Nothing Then
        Set RefCombinedRange = AddRange
    Else
        Set RefCombinedRange = Union(CombinedRange, AddRange)
    End If
End Function
  • Related