Defining a range for 2 private subs that have been merged - vba

I have 2 private subs that have been merged into the code below. the 2nd part of the code i need to define the logic for the range. The issue i am facing is my second code does not specify a range so i am unsure on how to define the range? and my VBA knowledge is not that great! can someone help me enter this information?
The Codes before merging are:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rng As Range, cel As Range
Set rng = Intersect(Target, Range([H2], Cells(Rows.Count,
"H").End(xlUp)))
If rng Is Nothing Then Exit Sub
Application.EnableEvents = False
rng.Offset(, 1).FormulaR1C1 = "=IF(RC[-1]<>"""",R1C[6] & ""-"" &" &
"TEXT(COUNTA(R2C[-1]:RC[-1]),""0000"") & ""-"" & R1C[7],"""")"
Application.EnableEvents = True
End Sub
Code 1 is using P1 and O1 to populate an automatic number in column I if information is provided in H Code 2:
Private Sub Move_blanks_To_Bottom(ByVal Target As Range)
If Target.CountLarge > 1 Then Exit Sub
If Target.Column <> 9 Then Exit Sub
Range("A1", Range("A" & Rows.Count).End(xlUp)).Resize(, 11).Sort
key1:=Range("I1"), order1:=xlAscending, Header:=xlYes
End Sub
Code 2 is using column I and sorting the values so if there is a value in I it moves the row to the next available line where column I is completed effectively if cell I is blank the row is moved to the bottom.
The merged code is:
Private Sub Worksheet_Change(ByVal Target As Range)
Application.EnableEvents = False
'Do logic for this first range
Dim rng As Range, cel As Range
Set rng = Intersect(Target, Range([H2], Cells(Rows.Count,
"H").End(xlUp)))
If Not Intersect(rng, Target) Is Nothing Then
rng.Offset(, 1).FormulaR1C1 = "=IF(RC[-1]<>"""",R1C[6] & ""-"" &" &
"TEXT(COUNTA(R2C[-1]:RC[-1]),""0000"") & ""-"" & R1C[7],"""")"
End If
'now do logic for the second range (move_blanks_to_bottom)
'2nd LOGIC HERE
If Target.CountLarge = 1 And Target.Column = 9 Then
Range("A1", Range("A" & Rows.Count).End(xlUp)).Resize(, 11).Sort
key1:=Range("I1"), order1:=xlAscending, Header:=xlYes
End If
Application.EnableEvents = True
End Sub
Thank you!

Your sorting command is fine though it is pretty complicated because it compresses a series of operations in one command. With this command you sort a range from column A to column K (=11) and from row 1 to last used row, with header. You could split this command with these simpler ones:
Dim lastrow As Long
Dim r As Range
lastrow = Range("A" & Rows.Count).End(xlUp).Row ' find row of last non-blank cell in column A
Set r = Range("A1").Resize(lastrow, 11) ' set the exact data range
r.Sort key1:=Range("I1"), order1:=xlAscending, Header:=xlYes
but this will do the same as in your code 2.
You might need to know that if you break the command into 2 lines you need to terminate the line with an _ like this:
Range("A1", Range("A" & Rows.Count).End(xlUp)).Resize(, 11).Sort _
key1:=Range("I1"), order1:=xlAscending, Header:=xlYes
otherwise the compiler will treat them as separate commands and will drop a Syntax error message.

Related

Merging 2 private sub VBA codes

i have 2 Private Sub Worksheet_change(ByVal Target As Range) codes that work on their own. i need them to work in the same sheet. whenever i do this the 2nd code does not run. how do i merge these please!!?
Code 1:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rng As Range, cel As Range
Set rng = Intersect(Target, Range([H2], Cells(Rows.Count,
"H").End(xlUp)))
If rng Is Nothing Then Exit Sub
Application.EnableEvents = False
rng.Offset(, 1).FormulaR1C1 = "=IF(RC[-1]<>"""",R1C[6] & ""-"" &" &
"TEXT(COUNTA(R2C[-1]:RC[-1]),""0000"") & ""-"" & R1C[7],"""")"
Application.EnableEvents = True
End Sub
Code 1 is using P1 and O1 to populate an automatic number in column I if information is provided in H
Code 2:
Private Sub Move_blanks_To_Bottom(ByVal Target As Range)
If Target.CountLarge > 1 Then Exit Sub
If Target.Column <> 9 Then Exit Sub
Range("A1", Range("A" & Rows.Count).End(xlUp)).Resize(, 11).Sort
key1:=Range("I1"), order1:=xlAscending, Header:=xlYes
End Sub
Code 2 is using column I and sorting the values so if there is a value in I it move the row to the next available line where column I is completed effectively if cell I is blank the row is moved to the bottom.
as i understand you cannot run 2 private sub codes so how would i run both of these on the same sheet at the same time?
thanks!
Because your first code exits (Exit Sub) when it fails that Intersect, then you have to call your second subroutine above that If statement. You'll have to pass it the Target as well like:
Call Move_blanks_To_Bottom(Target)
However, I think a rewrite might be best. Instead of exiting the subroutine all over the place, instead place relevant bits of code inside If statements so your routine can run to completion and exit gracefully:
Private Sub Worksheet_Change(ByVal Target As Range)
Application.EnableEvents = False
'Do logic for this first range
Dim rng As Range
rng = Range([H2], Cells(Rows.Count, "H").End(xlUp)))
If Not Intersect(rng, Target) Is Nothing Then
rng.Offset(, 1).FormulaR1C1 = "=IF(RC[-1]<>"""",R1C[6] & ""-"" &" & "TEXT(COUNTA(R2C[-1]:RC[-1]),""0000"") & ""-"" & R1C[7],"""")"
End If
'now do logic for the second range (move_blanks_to_bottom)
If Target.CountLarge = 1 And Target.Column = 9 Then
Range("A1", Range("A" & Rows.Count).End(xlUp)).Resize(, 11).Sort key1:=Range("I1"), order1:=xlAscending, Header:=xlYes
End If
Application.EnableEvents = True
End Sub

Copy & paste inadvertently triggers Worksheet_Change sub

I am having problems with a "Worksheet_Change" sub that copies and pastes the whole row into a second worksheet ("Completed") when the column "P" takes on the value "x". It reads like this:
Private Sub Worksheet_Change(ByVal Target As Range)
On Error Resume Next
Application.EnableEvents = False
'If Cell that is edited is in column P and the value is x then
If Target.Column = 16 And Target.Value = "x" Then
'Define last row on completed worksheet to know where to place the row of data
LrowCompleted = Sheets("Completed").Cells(Rows.Count, "A").End(xlUp).Row
'Copy and paste data
Range("A" & Target.Row & ":P" & Target.Row).Copy Sheets("Completed").Range("A" & LrowCompleted + 1)
'Delete Row from Project List
Range("A" & Target.Row & ":P" & Target.Row).Delete xlShiftUp
End If
Application.EnableEvents = True
End Sub
The sub itself works fine but if I copy and paste anywhere in the worksheet, the sub is activated and the row into which I paste is send to my "Completed" sheet.
I have played around with the "if-clause" without any luck so far. E.g.:
If Not Target.Column = 16 And Target.Value = "x" Is Nothing Then
I fear I am missing the obvious and I am grateful for any help.
Thanks and regards
PMHD
If you are concerned with muliple targets, deal with them; don't discard them.
Private Sub Worksheet_Change(ByVal Target As Range)
If not intersect(target, range("p:p")) is nothing then
on error goto meh
Application.EnableEvents = False
dim t as range, lrc as long
lrc = workSheets("Completed").Cells(Rows.Count, "A").End(xlUp).Row + 1
for each t in intersect(target, range("p:p"))
if lcase(t.Value2) = "x" Then
intersect(columns("A:P"), t.rows(t.row)).Copy _
destination:=workSheets("Completed").cells(lrc , "A")
lrc = lrc+1
'Delete Row from Project List
intersect(columns("A:P"), t.rows(t.row)).Delete xlShiftUp
end if
next t
End if
meh:
Application.EnableEvents = true
end sub
Thanks, Jeeped.
The problem arose due to Target referring to multiple cells. It was fixed by excluding cases where Target.Count > 1.
Private Sub Worksheet_Change(ByVal Target As Range)
On Error Resume Next
Application.EnableEvents = False
'Exclude all cases where more than one cell is Target
If Target.Count > 1 Then
'If Cell that is edited is in column P and the value is x then
ElseIf Target.Column = 16 And Target.Value = "x" Then
'Define last row on completed worksheet to know where to place the row of data
LrowCompleted = Sheets("Completed").Cells(Rows.Count, "A").End(xlUp).Row
'Copy and paste data
Range("A" & Target.Row & ":P" & Target.Row).Copy Sheets("Completed").Range("A" & LrowCompleted + 1)
'Delete Row from Project List
Range("A" & Target.Row & ":P" & Target.Row).Delete xlShiftUp
End If
Application.EnableEvents = True
End Sub

Removing all rows that contain specific text over multiple sheets

I have four sheets with raw data that I would like to be duplicated in my workbook and left alone for cross reference. Then I would like to remove all rows above the cell with the text "proj def" (it appears twice, but there are cells that lie in between the two appearances - which will be evident in my code). I would like to do this for the first four sheets of my workbook while leaving the original duplicated worksheets alone but am only able to do so with the first worksheet labeled "ptd". I have tried to activate the next worksheet "ytd" and even delete the original worksheet "ptd" to see if it would allow me to change the location of myRange but I have had no success. Essentially I want to run this code in sub methods, two for the first sheet "ptd", two more for the second sheet "ytd", another 2 for "qtr" and the final 2 for "mth". Any edits to my sample code would be much appreciated.
Sub part1()
Worksheets("ptd").Copy After:=Worksheets("mth")
Worksheets("ytd").Copy After:=Worksheets("ptd (2)")
Worksheets("qtr").Copy After:=Worksheets("ytd (2)")
Worksheets("mth").Copy After:=Worksheets("qtr (2)")
End Sub
Sub part2()
Worksheets("ptd").Activate
Set rngActiveRange = ActiveCell
Dim MyRange As Range
Set MyRange = ActiveSheet.Range("A:A")
MyRange.Find("Customer Unit", LookIn:=xlValues).Select
rngActiveRange.Offset(-1, 0).Select
Range(rngActiveRange.Row & ":" & 1).Rows.Delete
End Sub
Sub part3()
Dim MyRange As Range
Set MyRange = ActiveSheet.Range("A:A")
MyRange.Find("Project Definition", LookIn:=xlValues).Select
ActiveCell.Offset(-1, 0).Select
Range(ActiveCell.Row & ":" & 1).Rows.Delete
End Sub
Sub part4()
Worksheets("ytd").Activate
Set rngActiveRange = ActiveCell
Dim MyRange As Range
Set MyRange = ActiveSheet.Range("A:A")
MyRange.Find("Customer Unit", LookIn:=xlValues).Select
rngActiveRange.Offset(-1, 0).Select
Range(rngActiveRange.Row & ":" & 1).Rows.Delete
End Sub
Sub part5()
Dim MyRange As Range
Set MyRange = ActiveSheet.Range("A:A")
MyRange.Find("Project Definition", LookIn:=xlValues).Select
ActiveCell.Offset(-1, 0).Select
Range(ActiveCell.Row & ":" & 1).Rows.Delete
End Sub
If I understand correctly, the below should work. The main thing I did was re-write with avoiding the use of .Select/.Activate.
Sub remove_Rows()
Dim ws As Worksheet
Dim foundCel As Range
' Copy sheets
Worksheets("ptd").Copy After:=Worksheets("mth")
Worksheets("ytd").Copy After:=Worksheets("ptd (2)")
Worksheets("qtr").Copy After:=Worksheets("ytd (2)")
Worksheets("mth").Copy After:=Worksheets("qtr (2)")
' Start removing rows
For Each ws In ActiveWorkbook.Worksheets
With ws
If InStr(1, .Name, "(") = 0 Then
Set foundCel = .Range("A:A").Find("Customer Unit", LookIn:=xlValues)
.Range(foundCel.Offset(-1, 0).Row & ":" & 1).Rows.Delete
Set foundCel = .Range("A:A").Find("Project Definition", LookIn:=xlValues)
.Range(foundCel.Offset(-1, 0).Row & ":" & 1).Rows.Delete
End If
End With
Next ws
End Sub

Need to Delete data correctly in column F if met empty cells on G

My code is limited to work fine only with first empty cell found, the problem starts is that if finds the next two or more empty cells because it loops a bit (I can handle with that), but if it finds empty cells and next finds cells with data again, it totally fails.
Private Sub Worksheet_Change(ByVal Target As Range)
firstRow = 7
lastrow = Sheets("Datos del Proyecto").Range("F" & Rows.Count).End(xlUp).row
i = firstRow
Do Until i > lastrow
If Sheets("Datos del Proyecto").Range("G" & i).Value Like "" Then
Sheets("Datos del Proyecto").Range("F" & i).ClearContents
End If
i = i + 1
Loop
Screenshot:
Since the code is placed inside "Datos del Proyecto" sheet, in Worksheet_Change event, there is no need to reference it in the code all the time, as it is the default sheet.
Using Application.EnableEvents = False will prevent the code to exit and re-enter the Sub as you ClearContents each iteration inside the For loop.
Code
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Dim LastRow As Long, i As Long
' disable worksheet events >> will prevent the endless loop you got
Application.EnableEvents = False
' find last row in Column F
LastRow = Cells(Rows.Count, "F").End(xlUp).Row
' loop through all rows from row 7 until last row
For i = 7 To LastRow
If IsEmpty(Range("G" & i)) Or Range("G" & i).Value = "" Then
Range("F" & i).ClearContents
End If
Next i
Application.EnableEvents = True
End Sub
You can ass an option to your Sub , to make it run only if the change occurred in certain Range by adding these 3 lines in the beginning of the code:
Dim WatchRange As Range
' check only if cells changed are in Column G
Set WatchRange = Columns("G:G")
If Not Intersect(Target, WatchRange) Is Nothing Then
#Shai_Rado answer:
'Option Explicit <-- I needed to disable to make it work.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim LastRow As Long, i As Long
'Dim WatchRange As Range <-- I needed to disable to make it work.
'Set WatchRange = Columns("G:G") <-- I needed to disable to make it work.
'If Not Intersect(Target, WatchRange) Is Nothing Then <-- I needed to disable to make it work.
Application.EnableEvents = False
LastRow = Cells(Rows.Count, "F").End(xlUp).row
For i = 7 To LastRow
If IsEmpty(Range("G" & i)) Or Range("G" & i).Value = "" Then
Range("F" & i).ClearContents
End If
Next i
Application.EnableEvents = True
End Sub
The one I proposed with the help lines:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim i As Long
Application.EnableEvents = False
firstRow = 7
lastrow = Sheets("Datos del Proyecto").Range("F" & Rows.Count).End(xlUp).row
i = firstRow
Do Until i > lastrow
If Sheets("Datos del Proyecto").Range("G" & i).Value Like "" Then
Sheets("Datos del Proyecto").Range("F" & i).ClearContents
End If
i = i + 1
Loop
Application.EnableEvents = True
End Sub

Excel VBA deleting entire row based on multiple conditions in a column

I am trying to write a macro in vba for excel. I want to delete every row that does not have at least one of three keywords in column D (keywords being "INVOICE", "PAYMENT", or "P.O."). I need to keep every row that contains these keywords. All other rows need to be deleted and the rows remaining need to be pushed to the top of the document. There are also two header rows that can not be deleted.
I found the code below but it deletes every row that does not contain "INVOICE" only. I can not manipulate the code to do what I need it to do.
Sub Test()
Dim ws As Worksheet
Dim rng1 As Range
Dim lastRow As Long
Set ws = ActiveWorkbook.Sheets("*Name of Worksheet")
lastRow = ws.Range("D" & ws.Rows.Count).End(xlUp).Row
Set rng = ws.Range("D1:D" & lastRow)
' filter and delete all but header row
With rng
.AutoFilter Field:=1, Criteria1:="<>*INVOICE*"
.Offset(2, 0).SpecialCells(xlCellTypeVisible).EntireRow.Delete
End With
' turn off the filters
ws.AutoFilterMode = False
End Sub
I would approach this loop slightly different. To me this is a bit easier to read.
Sub Test()
Dim ws As Worksheet
Dim lastRow As Long, i As Long
Dim value As String
Set ws = ActiveWorkbook.Sheets("*Name of Worksheet")
lastRow = ws.Range("D" & ws.Rows.Count).End(xlUp).Row
' Evaluate each row for deletion.
' Go in reverse order so indexes don't get messed up.
For i = lastRow To 2 Step -1
value = ws.Cells(i, 4).Value ' Column D value.
' Check if it contains one of the keywords.
If Instr(value, "INVOICE") = 0 _
And Instr(value, "PAYMENT") = 0 _
And Instr(value, "P.O.") = 0 _
Then
' Protected values not found. Delete the row.
ws.Rows(i).Delete
End If
Next
End Sub
The key here is the Instr function which checks for your protected keywords within the cell value. If none of the keywords are found then the If condition is satisfied and the row is deleted.
You can easily add additional protected keywords by just appending to the If conditions.
'similar with previous post, but using "like" operator
Sub test()
Dim ws As Worksheet, i&, lastRow&, value$
Set ws = ActiveWorkbook.ActiveSheet
lastRow = ws.Range("D" & ws.Rows.Count).End(xlUp).Row
For i = lastRow To 2 Step -1
value = ws.Cells(i, 4).value
' Check if it contains one of the keywords.
If Not (value Like "*INVOICE*" _
Or value Like "*PAYMENT*" _
Or value Like "*P.O.*") _
Then
' Protected values not found. Delete the row.
ws.Rows(i).Delete
End If
Next
End Sub
'
Sub test()
Dim i&
Application.ScreenUpdating = False
i = Range("D" & Rows.Count).End(xlUp).Row
While i <> 1
With Cells(i, 4)
If Not (.value Like "*INVOICE*" _
Or .value Like "*PAYMENT*" _
Or .value Like "*P.O.*") _
Then
Rows(i).Delete
End If
End With
i = i - 1
Wend
Application.ScreenUpdating = True
End Sub
The othe way is to insert an IF test in a working column, and then AutoFilter that.
This is the VBA equivalent of entering
=SUM(COUNTIF(D1,{"*INVOICE*","*PAYMENT*","*P.O.*"}))=0
and then deleting any row where none of these values are found in the corrresponing D cell
Sub QuickKill()
Dim rng1 As Range, rng2 As Range, rng3 As Range
Set rng1 = Cells.Find("*", , xlValues, , xlByColumns, xlPrevious)
Set rng2 = Cells.Find("*", , xlValues, , xlByRows, xlPrevious)
Set rng3 = Range(Cells(rng2.Row, rng1.Column), Cells(1, rng1.Column))
Application.ScreenUpdating = False
Rows(1).Insert
With rng3.Offset(-1, 1).Resize(rng3.Rows.Count + 1, 1)
.FormulaR1C1 = "=SUM(COUNTIF(RC[-1],{""*INVOICE*"",""*PAYMENT*"",""*P.O.*""}))=0"
.AutoFilter Field:=1, Criteria1:="TRUE"
.EntireRow.Delete
On Error Resume Next
'in case all rows have been deleted
.EntireColumn.Delete
On Error GoTo 0
End With
Application.ScreenUpdating = True
End Sub