How to automatically run VBA macro on cell change - excel

I have searched extensively for a solution to this but nothing I've tried works automatically. The goal is that anytime text is manually entered in column C, the macro will find the last number used in column CG, increment by 1 if less than 6 and then enter that value on the active row. This works when run manually, but I cannot figure out how to automatically trigger when data is entered in column C.
Sub Counting()
Dim rng As Range
Dim text As String
Dim counterNumber As Integer
Dim counter As Range
Dim ws As Worksheet
Set ws = ActiveWorkbook.Sheets("Sheet1")
Set rng = ws.Cells(ActiveCell.Row, 3)
text = ""
If rng <> text Then
Set counter = rng
Set counter = counter.Offset(-1, 82) 'Finds the last value entered in the Counter column
counterNumber = counter 'Temporary storage for counter number
If counterNumber = 6 Then 'Restarts counting loop
counterNumber = 0
End If
counterNumber = counterNumber + 1 'Increase counter number by 1
Set counter = counter.Offset(1, 0) 'Returns to the active row
counter = counterNumber 'Inputs the updated counter number
End If
End Sub

Please, copy next code in the sheet to be processed code module. To do that, copy the code, right click on the sheet name, choose View Code and paste:
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Column = 3 Then 'treat only changes in column C:C
Dim counterNumber As Integer, counter As Range, lastR As Long
If Target.Value <> "" Then
Set counter = Me.Range("CG" & Target.row - 1) 'the counter to be updated
counterNumber = counter.Value 'Temporary storage for counter number
If counterNumber = 6 Then 'Restarts counting loop
counterNumber = 0
End If
counterNumber = counterNumber + 1 'Increment counter by 1
counter.Offset(1, 0).Value = counterNumber 'Inputs the incremented number
End If
End If
End Sub

Increment On Cell Change
Standard Module e.g. Module1
Option Explicit
Sub IncrementInColumn( _
ByVal Target As Range)
' Source
Const sCol As String = "C"
Const sNotCriteria As String = ""
' Destination
Const dCol As String = "CG"
Const dCounterMin As Long = 1
Const dCounterMax As Long = 6
Const dNotCriteria As String = "" '***
' Or maybe:
'Const dNotCriteria As Long = 0
Dim ws As Worksheet: Set ws = Target.Worksheet
Dim srg As Range: Set srg = Intersect(Target, ws.Columns(sCol))
If srg Is Nothing Then Exit Sub
Application.ScreenUpdating = False
Application.EnableEvents = False ' to stop retriggering the event procedure
On Error GoTo ClearError ' to enable the settings if something goes wrong
Dim sCell As Range
Dim dCell As Range
Dim dValue As Variant
For Each sCell In srg.Cells
Set dCell = sCell.EntireRow.Columns(dCol)
If CStr(sCell.Value) <> sNotCriteria Then
dValue = dCell.Value
If IsNumeric(dValue) Then
If dValue >= dCounterMax Or dValue < dCounterMin Then
dCell.Value = dCounterMin
Else
dCell.Value = Int(dValue) + 1
End If
Else
dCell.Value = dCounterMin
End If
Else
dCell.Value = dNotCriteria '***
End If
Next sCell
SafeExit:
Application.EnableEvents = True
Application.ScreenUpdating = True
Exit Sub
ClearError:
Debug.Print "Run-time error '" & Err.Number & "': " & Err.Description
Resume SafeExit
End Sub
' Just a Manual Test
Sub IncrementColumnTEST()
If Not TypeName(Selection) = "Range" Then Exit Sub
IncrementInColumn Selection
End Sub
Sheet Module e.g. Sheet1
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
IncrementInColumn Target
End Sub

Related

Automate macro when specified cell range is blank

Been working/researching on this code I am developing for my workplace tasking sheet. First part calls for the 'movebasedonvalue' macro when column F indicates task is closed. Second part, what my goal is to reassign a new UID with the macro 'NewUID', which as a stand alone works; I am attempting to have it called as soon as a cell in specified range within column B is blank.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim Z As Long
Dim xVal As String
On Error Resume Next
If Intersect(Target, Range("F:F")) Is Nothing Then Exit Sub
Application.EnableEvents = False
For Z = 1 To Target.Count
If Target(Z).Value > 0 Then
Call movebasedonvalue 'Macro to select row and move row content to specified sheet
End If
Next
Application.EnableEvents = True
End Sub
Private Sub FillBlanks(ByVal Target As Range)
Dim rngBlanks As Range
Dim ws As Worksheet
Set rngBlanks = Range("B4:B8,B10:B14,B16:20") 'Specifying the range
Set ws = ThisWorkbook.Worksheets("Burnout_Chart") 'Specifing Worksheet
With ws
If WorksheetFunction.CountBlank(rngBlank) > 0 Then 'wanting to identify blank cells in specified range
For Each area In rngBlanks.SpecialCells(xlCellTypeBlanks).Areas 'Trying to
Call NewUID 'Inputs new Unique ID into blank cell of Column B
Next
End If
End With
End Sub
Here is my movebasedonvalue code:
Sub movebasedonvalue()
Dim xRg As Range
Dim xCell As Range
Dim A As Long
Dim B As Long
Dim C As Long
A = Worksheets("Burnout_Chart").UsedRange.Rows.Count
B = Worksheets("Completed").usedRange.Rows.Count
If B = 1 Then
If Application.WorksheetFunction.CountA(Worksheets("Completed").UsedRange) = 0 Then B = 0
End If
Set xRg = Worksheets("Burnout_Chart").Range("F4:F" & A)
On Error Resume Next
Application.ScreenUdating = False
For C = 1 To xRg.Count
If CStr(xRg(C).Value) = "Closed" Then
xRg(C).EntireRow.Copy Destination:=Worksheets("Completed").Range("A" & B + 1)
xRg(C).EntireRow.ClearContents
If CStr(xRg(C).Value) = "Closed" Then
C = C - 1
End If
B = B + 1
End If
Next
Application.ScreenUpdating = True
End Sub
Here is my NewUID code:
Sub NewUID(c As Range)
Dim AR As Long
Dim MaxID As Long
Dim NewID As Long
Dim Burnout As Worksheet
Dim UID As Range
Set Burnout = ThisWorkbook.Sheets("Burnout_Chart")
Set UID = Range("B4:B8,B10:B14,B16:B20")
MaxID = Application.WorksheetFunction.Max(UID)
NewID = MaxID + 1
AR = ActiveCell.Row
ActiveCell.Value = NewID 'code to add id to cell c
End Sub
EDIT3: my last guess
Something like this should work
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rng As Range, c As Range, rngUID As Range, nextID, nextRow As Long
Dim wsComp As Worksheet
On Error GoTo haveError
Set rng = Intersect(Target, Me.Range("F:F"))
If Not rng Is Nothing Then
Set wsComp = ThisWorkbook.Worksheets("Completed")
nextRow = NextEmptyRow(wsComp)
Application.EnableEvents = False
For Each c In rng.Cells
If c.Value = "Closed" Then
With c.EntireRow
.Copy wsComp.Cells(nextRow, "A")
.ClearContents
nextRow = nextRow + 1
End With
End If
Next c
Application.EnableEvents = True
End If
Set rngUID = Me.Range("B4:B8,B10:B14,B16:B20")
Set rng = Intersect(Target, rngUID)
If Not rng Is Nothing Then
nextID = Application.Max(rngUID) + 1 'next ID
Application.EnableEvents = False
For Each c In rng.Cells
If Len(c.Value) = 0 Then 'if cell is blank then assign an ID
c.Value = nextID
nextID = nextID + 1
End If
Next c
Application.EnableEvents = True
End If
Exit Sub
haveError:
Application.EnableEvents = True 'make sure events are re-enabled
End Sub
'given a worksheet, return the row number of the next empty row
Function NextEmptyRow(ws As Worksheet) As Long
Dim f As Range
Set f = ws.Cells.Find(What:="*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious)
If f Is Nothing Then
NextEmptyRow = 1
Else
NextEmptyRow = f.Row + 1
End If
End Function
I figured out my issue, there's a lot that needs to be cleaned up but here is the code I got working for what I need:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim Z As Long
Dim xVal As String
Dim KeyCells As Range 'redundant (Choose one or the other)
Dim UID As Range 'redundant (Choose one or the other)
Dim AR As Long
Dim MaxID As Long
Dim NewID As Long
Dim Burnout As Worksheet
On Error Resume Next
Set KeyCells = Range("B4:B8,B10:14,B16:B20") 'redundant (Choose one or the other)
Set UID = Range("B4:B8,B10:B14,B16:B20") 'redundant (Choose one or the other)
Set Burnout = ThisWorkbook.Sheets("Burnout_Chart")
If Intersect(Target, Range("F:F")) Is Nothing Then Exit Sub
Application.EnableEvents = False
For Z = 1 To Target.Count
If Target(Z).Value > 0 Then
Call movebasedonvalue
End If
If Not Application.Intersect(KeyCells, Range(Target.Address)) _
Is Nothing Then
MaxID = Application.WorksheetFunction.Max(UID)
NewID = MaxID + 1
AR = ActiveCell.Row
Range("B" & AR).Select 'This is what I was missing
ActiveCell.Value = NewID
End If
Next
Application.EnableEvents = True
End Sub

Excel: Moving a Table Row to Another Sheet's Table

Not very experienced with Excel coding, but I got a decent way myself. But I cant figure this thing out.
Looking to automate a sheet so that when a cell in column E is set to "Staffed", the macro will move the entire row from the table in Sheet A to the Table in Sheet B. At the moment, the macro is placing the cut and pasted row below the table, sometimes 20 something rows below, on the new sheet.
I would appreciate the help. Thank you
Sub NtS()
Dim xRg As Range
Dim xCell As Range
Dim I As Long
Dim J As Long
Dim K As Long
I = Worksheets("Clients Needing Staffed").UsedRange.Rows.Count
J = Worksheets("Clients Staffed").UsedRange.Rows.Count
If J = 1 Then
If Application.WorksheetFunction.CountA(Worksheets("Clients Staffed").UsedRange) = 0 Then J = 0
End If
Set xRg = Worksheets("Clients Needing Staffed").Range("E1:E" & I)
On Error Resume Next
Application.ScreenUpdating = False
For K = 1 To xRg.Count
If CStr(xRg(K).Value) = "Staffed" Then
xRg(K).EntireRow.Copy Destination:=Worksheets("Clients Staffed").Range("A" & J + 1)
xRg(K).EntireRow.Delete
If CStr(xRg(K).Value) = "Staffed" Then
K = K - 1
End If
J = J + 1
End If
Next
Application.ScreenUpdating = True
End Sub
Copy and Delete Rows (Union)
Option Explicit
Sub NtS()
' Constants
' Source
Const srcName As String = "Clients Needing Staffed"
Const srcFirstRow As Long = 2
Const srcLastRowCol As Variant = "E" ' e.g. 1 or "A", 5 or "E"...
Const srcCritCol As Variant = "E" ' e.g. 1 or "A", 5 or "E"...
Const srcCriteria As String = "Staffed"
' Target
Const tgtName As String = "Clients Staffed"
' The following parameter has to be 1 or "A", because copying entire row.
Const tgtFirstCol As Variant = "A" ' e.g. 1 or "A"
Const tgtFirstRowCol As Variant = "A" ' e.g. 1 or "A"
' Other
Dim wb As Workbook: Set wb = ThisWorkbook ' The workbook with this code.
' Source
Dim src As Worksheet: Set src = wb.Worksheets(srcName)
Dim srcLastRow As Long
srcLastRow = src.Cells(src.Rows.Count, srcLastRowCol).End(xlUp).Row
If srcLastRow < srcFirstRow Then Exit Sub
' Define Criteria column range.
Dim rng As Range: Set rng = src.Range(src.Cells(srcFirstRow, srcCritCol), _
src.Cells(srcLastRow, srcCritCol))
Application.ScreenUpdating = False
' Loop through each cell of Criteria column range to search
' for Criteria value.
Dim cel As Range, uRng As Range, i As Long, k As Long
For Each cel In rng.Cells
If cel.Value = srcCriteria Then
k = k + 1
If Not uRng Is Nothing Then
Set uRng = Union(uRng, cel.EntireRow)
Else
Set uRng = cel.EntireRow
End If
End If
Next cel
If uRng Is Nothing Then
MsgBox "Data has already been deleted.", vbExclamation, "Done Nothing"
Exit Sub
End If
' Target
' Define Target First Row.
Dim tgt As Worksheet: Set tgt = wb.Worksheets(tgtName)
Dim tgtFirstRow As Long
tgtFirstRow = tgt.Cells(tgt.Rows.Count, tgtFirstRowCol).End(xlUp).Row + 1
' Copy 'collected' range of rows to Target worksheet starting
' from Target First Cell.
uRng.Copy tgt.Cells(tgtFirstRow, tgtFirstCol)
' Delete 'collected' range of rows.
uRng.EntireRow.Delete
' Finish
Application.ScreenUpdating = True
' Inform user.
MsgBox "Data copied and deleted.", vbInformation, "Success"
End Sub

Delete rows based on values not found in another sheet

I am trying to does the following:
Compare the value (a string of characters) that is stored in column B of worksheet "State = Closed", to all the values in column A of another worksheet called "Match List".
Delete any row in the "State = Closed" that does not have a match to the corresponding "Match List" value.
The code needs to work with any length (as the number of rows will change) list in "Match List", as well as any length "State = Closed" worksheet.
Sub ListRemove()
Application.ScreenUpdating = False
Dim i As Integer
Dim b As Integer
Dim Lastrow As Long
Dim Lastrowb As Long
Dim Del As Variant
Worksheets("Match List").Activate
Set Del = Range("A1:A67") '<--- This needs to be modified to work with any length Match List
Lastrowb = Worksheets("State = Closed").Cells(Rows.Count, "A").End(xlUp).Row
Lastrow = Cells(Rows.Count, "A").End(xlUp).Row
For i = 1 To Lastrow
For b = 1 To Lastrowb
If Worksheets("State = Closed").Cells(i, 2).Value <> Del(b) Then
Worksheets("State = Closed").Rows(i).EntireRow.Delete
End If
Next
Next
Application.ScreenUpdating = True
Worksheets("State = Closed").Activate
End Sub
This deletes every row in the "State = Closed" worksheet instead of just the rows that do not contain a corresponding value in the Match List worksheet.
Find my code below. Two for-loops to check for each value if there is an identical entry in any cell of the second sheet.
Sub List_Remove()
Dim i As Integer
Dim j As Integer
Dim k As Boolean
Dim shA As Worksheet
Dim shB As Worksheet
Set shA = Sheets("Sheet1") 'Worksheet that you want to compare with
Set shB = Sheets("Sheet2") 'Worksheet you want to delete rows from
For i = shB.UsedRange.Rows.Count To 1 Step -1
k = False
For j = 1 To shA.UsedRange.Rows.Count
If shB.Cells(i, 1).Value = shA.Cells(j, 1).Value Then
k = True
End If
Next
If k = False Then
shB.Rows(i).Delete
End If
Next
EndSub
This code is tested. Note use of working directly with objects.
Option Explicit
Sub ListRemove()
Application.ScreenUpdating = False
Dim matchList As Worksheet
Set matchList = Worksheets("Match List")
Dim matchRange As Range
Set matchRange = matchList.Range("A1:A" & matchList.Cells(matchList.Rows.Count, 1).End(xlUp).Row)
Dim closedList As Worksheet
Set closedList = Worksheets("State = Closed")
Dim searchRows As Long
searchRows = closedList.Cells(closedList.Rows.Count, 1).End(xlUp).Row
Dim i As Long
For i = searchRows To 1 Step -1
If IsError(Application.Match(closedList.Cells(i, 1).Value, matchRange, 0)) Then
closedList.Cells(i, 1).EntireRow.Delete
End If
Next
End Sub
Delete Rows (Union)
The Code
Option Explicit
Sub ListRemove()
Application.ScreenUpdating = False
' Constants
Const mlName As String = "Match List"
Const mlFR As Long = 1
Const mlCol As Variant = "A" ' e.g. 1 or "A"
Const scName As String = "State = Closed"
Const scFR As Long = 1
Const scCol As Variant = "B" ' e.g. 1 or "A"
' Workbook
Dim wb As Workbook: Set wb = ThisWorkbook
' Match List
Dim ml As Worksheet: Set ml = wb.Worksheets(mlName)
Dim mlLR As Long: mlLR = ml.Cells(ml.Rows.Count, mlCol).End(xlUp).Row
Dim Del As Variant
Del = ml.Range(ml.Cells(mlFR, mlCol), ml.Cells(mlLR, mlCol)).Value
' State = Closed
Dim sc As Worksheet: Set sc = wb.Worksheets(scName)
Dim scLR As Long: scLR = sc.Cells(sc.Rows.Count, scCol).End(xlUp).Row
Dim rng As Range
Set rng = sc.Range(sc.Cells(scFR, scCol), sc.Cells(scLR, scCol))
' Collecting Cells
Dim cel As Range, URng As Range
For Each cel In rng.Cells
If IsError(Application.Match(cel.Value, Del, 0)) Then
GoSub collectCells
End If
Next
' Deleting Rows
'If Not URng Is Nothing Then URng.EntireRow.Delete
' First test with Hiding Rows.
If Not URng Is Nothing Then URng.EntireRow.Hidden = True
Application.ScreenUpdating = True
sc.Activate
Exit Sub
collectCells:
If Not URng Is Nothing Then
Set URng = Union(URng, cel)
Else
Set URng = cel
End If
Return
End Sub

Need to increase value 1 by 1 in active cell within a range

Sub Macro5()
Dim rng As Range
Set rng = Selection
For Each cell In rng
ActiveCell.Value = ActiveCell.Value + 1
Next
End Sub
Quick fix for your code would be
Sub Macro5()
Dim rng As Range
Set rng = Range("B2:B10")
Dim cell As Range
For Each cell In rng
cell.Value = cell.Value + 1
Next
End Sub
Update: By the comment I guess you would like to use the SelectionChange Event. Put the following code into the code module of the sheet
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
On Error GoTo EH
Application.EnableEvents = False
Dim rg As Range
Set rg = Range("B2:B12")
If Not (Intersect(rg, Target) Is Nothing) Then
Dim sngCell As Range
' This will only increase the values of the selected cells within B2:B10
' Not sure if this is wanted. Otherwise just modify according to your needs
For Each sngCell In Intersect(Target, rg)
sngCell.Value = sngCell.Value + 1
Next sngCell
End If
EH:
Application.EnableEvents = True
End Sub
Update 2: If you want to run the code via a button put the following code into a standard module and assign it to a button you create on the sheet
Sub Increase()
On Error GoTo EH
Application.EnableEvents = False
Dim rg As Range
Set rg = Range("B2:B10")
If Not (Intersect(rg, Selection) Is Nothing) Then
Dim sngCell As Range
For Each sngCell In Intersect(Selection, rg)
sngCell.Value = sngCell.Value + 1
Next sngCell
End If
EH:
Application.EnableEvents = True
End Sub
Test if the current cell is within your range!
Sub Macro5()
Dim rng As Range
Dim fixed_rng As Range
Set rng = Selection
Set fixed_rng = Range("B1:B10")
if Application.Union(rng, fixed_rng) = fixed_rng then
For Each cell In rng
ActiveCell.Value = ActiveCell.Value + 1
Next
End If
End Sub

Finding column based on header then formatting rows

I am attempting to build a loop that searches through headers and finds a contained value, In this case, "Avg". If the value is found it will work down the column and apply a format based on a comparison to another column. I am trying to convert my cell variable in the For loop (Z) into a column address so I can use to control my ws.Cells() value in the next loop.
Any help is greatly appreciated, thanks!!!!
Sub foo()
Dim ws As Worksheet: Set ws = Sheets("Sheet1")
Dim Z As Range
lastRow = ws.Cells(ws.Rows.Count, "I").End(xlUp).Row
For Each Z In Range("I1:BM1").Cells
If InStr(1, Z.Value, "Avg") Then
For i = 2 To lastRow 'loop from row 2 to last
If ws.Cells(i, 8) - ws.Cells(i, Z) < 0 Then
ws.Cells(i, Z).Interior.ColorIndex = 4
End If
Next i
End If
Next Z
End Sub
It's not exactly clear to me what you want - but from the title it appears you want to get the column number based on the header text? If so, this will do that:
Private Function GetColumn(headerName As String) As Integer
Dim col As Integer
GetColumn = 0
For col = 1 To ActiveSheet.UsedRange.Columns.Count
If ActiveSheet.Cells(1, col).Value = headerName Then
GetColumn = col
Exit For
End If
Next col
End Function
Find Header and Format Cells
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: In a column range specified by its header,
' highlights the cells matching a condition.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Sub HighlightBelowAverages()
' Define constants.
Const PROC_TITLE As String = "Highlight Below-Averages"
Const COMPARE_COLUMN As String = "H"
Const AVG_SEARCH_COLUMNS As String = "I:BM"
Const AVG_COLUMN_HEADER As String = "Avg"
Const AVG_COLOR_INDEX As Long = 4 ' Bright Green
' Reference the Search range.
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
Dim ws As Worksheet: Set ws = wb.Sheets("Sheet1")
Dim srg As Range
' It is NOT assumed that the used range starts in row '1'.
Set srg = Intersect(ws.UsedRange, ws.Range(AVG_SEARCH_COLUMNS))
If srg Is Nothing Then
MsgBox "The Average search columns '" & AVG_SEARCH_COLUMNS _
& "' are not part of the used range.", vbCritical, PROC_TITLE
Exit Sub
End If
' Find the Average header cell.
Dim ahCell As Range
With srg
Set ahCell = .Find(AVG_COLUMN_HEADER, _
.Cells(.Rows.Count, .Columns.Count), xlFormulas, xlWhole, xlByRows)
End With
If ahCell Is Nothing Then
MsgBox "Header '" & AVG_COLUMN_HEADER & "' not found.", _
vbCritical, PROC_TITLE
Exit Sub
End If
' Reference the Average (single-column) range.
Dim afCell As Range: Set afCell = ahCell.Offset(1)
Dim alCell As Range
Set alCell = Intersect(srg.Rows(srg.Rows.Count), ws.Columns(ahCell.Column))
' It IS assumed that the data has one row of headers.
If afCell.Row > alCell.Row Then
MsgBox "No data found.", vbCritical, PROC_TITLE
Exit Sub
End If
Dim arg As Range: Set arg = ws.Range(afCell, alCell)
' Reference the Compare (single-column) range.
Dim crg As Range
' It is NOT assumed that the used range starts in column 'A'.
Set crg = Intersect(arg.EntireRow, ws.Columns(COMPARE_COLUMN))
' Highlight the cells.
Application.ScreenUpdating = False
arg.Interior.ColorIndex = xlNone
Dim aCell As Range, cCell As Range, r As Long
For Each aCell In arg.Cells
r = r + 1
Set cCell = crg.Cells(r)
If cCell.Value < aCell.Value Then ' Compare is less than Average
aCell.Interior.ColorIndex = AVG_COLOR_INDEX
End If
Next aCell
Application.ScreenUpdating = True
' Inform.
MsgBox "Below-averages highlighted.", vbInformation, PROC_TITLE
End Sub

Resources