I am writing VBA for a file at work and need to do something a little odd. I need to highlight the row (not the entire row, just the used part of the row) if the cell in Column J contains a certain value. I have figured everything out except my code is highlighting the entire row, and I only want it to highlight the used cells in that row. Can anyone advise? Code below
'Yellow Highlight..........THIS IS HIGHLIGHTING THE WHOLE ROW....WHY!!!!! WHY!!!!!!!!!!!
Sheets("EMM").Activate
With Sheets("EMM")
For Lrow = 1 To ActiveSheet.UsedRange.Rows.Count
With .Cells(Lrow, "J")
If Not IsError(.Value) Then
If .Value = "Desk to adjust" Then
.EntireRow.Interior.ColorIndex = 6
End If
End If
End With
Next Lrow
End With
With Sheets("EMM")
For Lrow = 1 To .UsedRange.Rows.Count
With .Cells(Lrow, "J")
If Not IsError(.Value) Then
If .Value = "Desk to adjust" Then
Sheets("EMM").UsedRange.Rows(Lrow).Interior.ColorIndex = 6
End If
End If
End With
Next
End With
or using AutoFilter
With Sheets("EMM").UsedRange
.Parent.AutoFilterMode = False
.AutoFilter
.AutoFilter Field:=10, Criteria1:="Desk to adjust"
.Rows(1).Hidden = True 'Header row
.Interior.ColorIndex = 6
.Rows(1).Hidden = False 'Header row
.AutoFilter
.Cells(1, 1).Activate
End With
The easiest to avoid looping on the row and fetching non-blank cells, or setting up a filter, is to do this inside the IF:
.UsedRange.Rows(lRow).Interior.ColorIndex = 6
.UsedRange.Rows(lRow).SpecialCells(xlCellTypeBlanks).Interior.ColorIndex = 0
(highlight the row then unhighlight empty cells)
Here is some commented code showing how to use the Range.AutoFilter method to achieve the results you're looking for. No looping required, so it is generally much more efficient:
Sub tgrFilter()
Dim ws As Worksheet
Dim rngData As Range
Dim strMatch As String
Set ws = ActiveWorkbook.Sheets("EMM") 'We will be working with sheet "EMM" in activeworkbook
Set rngData = ws.Range("J1").CurrentRegion 'Get the region that column J is a part of in order to limit the highlighting (so it doesn't highlight entire row)
strMatch = "Desk to adjust" 'This is the string/value we are looking for in column J
rngData.Interior.Color = xlNone 'Clear any previous highlighting
On Error Resume Next 'If there are no results, it would cause an error
'Work with just column J within rngData
With Intersect(rngData, ws.Columns("J"))
.AutoFilter 1, strMatch 'Note that the filter is not case sensitive
'Color the matching rows contained in the data (not the entire row)
Intersect(rngData, .Offset(1).Resize(.Rows.Count - 1).SpecialCells(xlCellTypeVisible).EntireRow).Interior.ColorIndex = 6
.AutoFilter 'Remove the filter
End With
If Err.Number <> 0 Then
'Error occurred which means there were no results
MsgBox "No matches found in column J for [" & strMatch & "]", , "No results"
Err.Clear
End If
On Error GoTo 0 'Remove the On Error Resume Next condition
End Sub
You can use Conditional Formatting instead of macro.
Select the cell your need to highlight and at menu bar go to Format -> Conditional Formatting
In dialog select condition you need to check, this example check if cell value is equal to 'AA'. You can add condition up to 3 conditions at a time
Next, Click format button to format cell when the condition is true.
After finish all click OK to close dialog you will get the highlight you need.
Related
I am stuck on a code where I apply a filter and then have to copy paste data from filtered rows to another sheet. But for some reason the code is not doing anything at all. I have applied an if condition but that is not working, it would be better if the condition was visible cells condition. Basically I want to apply filter>> then I want to copy cell in column 2 to another worksheet and perform calculation>> then copy calculated value in cell in column 7
Sub DOCFairshare()
Set ws = ThisWorkbook.Sheets("Final Orders") 'Setting worksheet in variable
ws.Activate
ws.AutoFilterMode = False 'Removing all filters
ActiveSheet.Range("$A$2:$EL$1561").AutoFilter Field:=50, Criteria1:= _
"DOC Planning Required" 'DOC Filter applied
i = 1
Do Until IsEmpty(Cells(i, 2))
If Cells(i, 50) = "DOC Planning Required" Then
Cells(i, 7).Copy
Worksheets("DOC Fairshare").Range("A3").PasteSpecial Paste:=xlPasteValues
Sheets("DOC Fairshare").Calculate
Worksheets("DOC Fairshare").Range("D11:U11").Copy
Worksheets("Final Orders").Cells(i, 7).PasteSpecial Paste:=xlPasteValues
Debug.Print Cells(i, 2)
End If
' Debug.Print Cells(i, 2)
i = i + 1
Loop
End Sub
I recommend to look at and use SpecialCells method in VBA help. I think it is very usefull.
In your case using like this example.
Range("A1:A10").SpecialCells(xlCellTypeVisible).Copy Range("C1")
It copies only visible cells to C1 from range A1-A10. I think more elegant then make loop and check if cell is visible and then copy which I used to do.
You do not say anything... I asume that my understanding should be correct. The code also assumes that on the second row there are not headers. If they exist, the line Set rngDocPl = ws.Range("AX2:AX1561")... should be adapted to Set rngDocPl = ws.Range("AX3:AX1561")....
Please, try the next code. It will stop after each iteration and shows in Immediate Window (Ctrl + G being in VBE) the value in G:G before calculations and after. Is it what you need? I cannot imagine what formulas you have in Worksheets("DOC Fairshare") and I cannot test anything:
Sub DOCFairshare()
Dim ws As Worksheet, wsDoc As Worksheet, rngDocPl As Range, cel As Range
Set ws = ThisWorkbook.Sheets("Final Orders") 'Setting worksheet in variable
Set wsDoc = Worksheets("DOC Fairshare") 'is this sheet in the same workbook?
ws.AutoFilterMode = False 'Removing all filters
ws.Range("$A$2:$EL$1561").AutoFilter field:=50, Criteria1:= _
"DOC Planning Required" 'DOC Filter applied
On Error Resume Next
Set rngDocPl = ws.Range("AX2:AX1561").SpecialCells(xlCellTypeVisible)
On Error GoTo 0
If rngDocPl Is Nothing Then MsgBox "No any visible cells in AX:AX column": Exit Sub
For Each cel In rngDocPl.cells
With wsDoc
Debug.Print "before, on row " & cel.row, cel.Offset(0, -43).value 'the cell in G:G before calculations
.Range("A3").value = cel.Offset(0, -43).value 'copy the value from G to "A3"
.Calculate
cel.Offset(0, -43).Resize(1, 19).value = .Range("D11:U11").value 'copy back the calculated range
Debug.Print "after, on row " & cel.row, cel.Offset(0, -43).value: Stop 'the cell in G:G after calculations
End With
Next
End Sub
I am trying to copy filtered data from one sheet to another, but for some reason I get a runtime error 1004 saying "to copy all cells from another worksheet to this worksheet make sure you paste them into the first cell (A1 or R1C1)" I actually don't want the header row copied, so all visible bar that row
What I am wanting is the copied data to be pasted to the first available row in the target sheet. Here is the code I have which filters for certain things, but then falls over on the paste line
Sub BBWin()
'
' BB Win Macro
' This macro will filter BB Win 1 - 8
'
With ActiveSheet.Range("A1").CurrentRegion
With .Resize(, .Columns.Count + 1)
With .Cells(2, .Columns.Count).Resize(.Rows.Count - 1)
.FormulaR1C1 = "=if(or(rc7={""K.BB_Win_1_2019"",""K.BB_Win_2_2019"",""K.BB_Win_3_2019"",""K.BB_Win_4_2019"",""K.BB_Win_5_2019"",""K.BB_Win_6_2019"",""K.BB_Win_7_2019"",""K.BB_Win_8_2019""}),""X"","""")"
.Value = .Value
End With
.HorizontalAlignment = xlCenter
End With
Cells.Select
Selection.SpecialCells(xlCellTypeVisible).Copy
Workbooks("Predictology-Reports.xlsx").Sheets("BB Reports").Range("A" & Rows.Count).End(xlUp).Offset(1).PasteSpecial xlPasteValues
End With
Application.CutCopyMode = False
End Sub
Any suggestions as to what is missing to have it work correctly?
=========================================
OK, perhaps I should have tried the question another way, posting the original working macro I was supplied, rather than posting my attempt to rewrite it.
This is basically the same thing as what I posted above, with the formula changed to look for different text, though it also has autofilter settings (which I don't need) and hides columns (which I don't need to do). This is working perfectly for me and does exactly what it is supposed to. I basically tried to duplicate it and remove the unwanted elements, but as you saw, found the error originally indicated. Obviously my limited knowledge caused the initial issue.
Sub Low_Risk()
'
' Low Risk Lays Macro
' This macro will filter for Remove VDW Rank 1, Class, Distance <=1650, # of Runners <=9, Exclude Brighton, Yarmouth, Windsor & Wolverhampton
'
With ActiveSheet.Range("A1").CurrentRegion
With .Resize(, .Columns.Count + 1)
With .Cells(2, .Columns.Count).Resize(.Rows.Count - 1)
.FormulaR1C1 = "=if(or(rc8={""Brighton"",""Yarmouth"",""Windsor"",""Wolverhampton""}),""X"","""")"
.Value = .Value
End With
.AutoFilter Field:=4, Criteria1:="<=9"
.AutoFilter Field:=11, Criteria1:="<=1650"
.AutoFilter .Columns.Count, "<>X"
.AutoFilter Field:=29, Criteria1:="<>1"
.HorizontalAlignment = xlCenter
End With
.Columns("C:C").EntireColumn.Hidden = True
.Columns("G:G").EntireColumn.Hidden = True
.Columns("I:I").EntireColumn.Hidden = True
.Columns("L:L").EntireColumn.Hidden = True
.Columns("N:W").EntireColumn.Hidden = True
.Columns("Y:AB").EntireColumn.Hidden = True
.Columns("AD:AJ").EntireColumn.Hidden = True
.Columns("AO:AO").EntireColumn.Hidden = True
.Columns("AQ:BQ").EntireColumn.Hidden = True
.Columns("BT:CP").EntireColumn.Hidden = True
.Parent.AutoFilter.Range.Offset(1).Copy
Workbooks("New Results File.xlsm").Sheets("Low Risk Lays").Range("A" & Rows.Count).End(xlUp).Offset(1).PasteSpecial xlPasteValues
End With
Application.CutCopyMode = False
End Sub
As indicated, this works absolutely perfectly, nested Withs and all. I can change the original formula so it is looking in the correct column and only for the text I want, but I obviously was not able to successfully remove the autofilter elements and the elements which hide columns without bringing up an error. I assume the removal of the .Parent.AutoFilter.Range.Offset(1).Copy line was the culprit, but wasn't sure how to approach the removal of the unwanted elements.
This original macro was supplied to me in one of the forums and I am loath to alter the formula part which does a good job of looking for the many text elements required to be copied. That was why I only looked to alter the autofilter section and hidden column section
I'm not sure if this helps at all, but it may clarify things a little
cheers and thanks so much for your effort
Cells.Select (with no leading period to tie it to the With block) will select all cells on whatever is the active sheet.
Try this (nested With's confuse me a bit, so removed a couple)
Sub BBWin()
Dim arr, ws As Worksheet, lc As Long, lr As Long
arr = Array("K.BB_Win_1_2019", "K.BB_Win_2_2019", "K.BB_Win_3_2019", _
"K.BB_Win_4_2019", "K.BB_Win_5_2019", "K.BB_Win_6_2019", _
"K.BB_Win_7_2019", "K.BB_Win_8_2019")
Set ws = ActiveSheet
'range from A1 to last column header and last row
lc = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column
lr = ws.Cells.Find("*", after:=ws.Range("A1"), LookAt:=xlPart, _
SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
With ws.Range("A1", ws.Cells(lr, lc))
.HorizontalAlignment = xlCenter
.AutoFilter Field:=7, Criteria1:=arr, Operator:=xlFilterValues
.Offset(1, 0).Resize(.Rows.Count - 1).SpecialCells(xlCellTypeVisible).Copy
End With
Workbooks("Predictology-Reports.xlsx").Sheets("BB Reports") _
.Range("A" & Rows.Count).End(xlUp).Offset(1).PasteSpecial xlPasteValues
Application.CutCopyMode = False
End Sub
Cells.Select selects all sheets cells.
Selection.SpecialCells(xlCellTypeVisible) keeps all cells, since nothing is hidden and everything is visible. You said something about "copy filtered data" but your code does not filter anything...
So, there is not place to paste all cells.
In order to make your code working, replace Cells.Select with .Cells.Select (the dot in front makes it referring to the resized UsedRange). Even if any selection is not necessary...
So, (better) use .cells.SpecialCells(xlCellTypeVisible).Copy...
Edited:
Your last code needs to only copy the visible cells of the filtered range. So, your code line
.Parent.AutoFilter.Range.Offset(1).Copy
must be replaced by the next one:
.Parent.AutoFilter.Range.Offset(1).SpecialCells(xlCellTypeVisible).Copy
or
.Offset(1).SpecialCells(xlCellTypeVisible).Copy
which refers the processed range (`UsedRange'), starting from the second row.
What I am wanting is the copied data to be pasted to the first
available row in the target sheet.
You should define your available row to paste your fillered rows in, or first blank row in the sheet you want the filtered data pasted. Then you will be able to paste your data into that row.
In my example, I'm filtering my datawork (source sheet) sheet by anything in col 24 that contains "P24128" and pasting into "Sheet8" (Target sheet), in my example.
I actually don't want the header row copied, so all visible bar that
row
You also didnt want the headers. :)
Sub CopyFilteredDataSelection10()
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Datawork")
ws.Activate
'Clear any existing filters
On Error Resume Next
ActiveSheet.ShowAllData
On Error GoTo 0
'1. Apply Filter
ActiveSheet.Range("A1:ADU5000").AutoFilter Field:=24, Criteria1:="*P24128*" ' "*" & "P24128" & "*" ' im filtering by anything in col 24 that contains "P24128"
'2. Copy Rows minus the header
Application.DisplayAlerts = False
ws.AutoFilter.Range.Copy 'copy the AF first
Set Rng = ws.UsedRange.Offset(1, 0)
Set Rng = Rng.Resize(Rng.Rows.Count - 1)
Rng.Copy
'3. The Sheet & Where you want to paste the filtered data precisely into Sheet x (Sheet 8 in my example)
Sheets("Sheet8").Activate
lr = ThisWorkbook.Worksheets("Sheet8").Cells(1, 1).SpecialCells(xlCellTypeLastCell).Row + 1
Range("A" & lr).Select
ActiveSheet.Paste
Application.DisplayAlerts = True
'4. Clear Filter from original sheet
On Error Resume Next
ws.Activate
ActiveSheet.ShowAllData
On Error GoTo 0
End Sub
What does the not-including the headers is this
ws.AutoFilter.Range.Copy 'copy the AutoFilter first
Set Rng = ws.UsedRange.Offset(1, 0)
Set Rng = Rng.Resize(Rng.Rows.Count - 1)
Rng.Copy
& your target is after you activate the target sheet and find its last row
lr = ThisWorkbook.Worksheets("Sheet8").Cells(1, 1).SpecialCells(xlCellTypeLastCell).Row + 1
I've been working on a code that will filter column CF for all "No" and then look to the cell to the right of the "No" to see if the number listed is "2". If the number is "2" then it will replace the contents of the cell in column D with "1 2 3".
So far the code is working for that purpose but I am lost on how to get it to go to the next "No" function in column CF and repeat the code for all "No"s. I am working with 50,000+ rows of data so every time I try to run an .offset function to find the next visible cell it crashes.
Any ideas on a loop to run the function so it jumps to the next "No" valuable visible in the autofilter?
Much appreciated.
Sub CorrectMealSwap()
ActiveSheet.Range("$CF$1").AutoFilter Field:=1, Criteria1:="No"
With Worksheets("Worksheet_1").AutoFilter.Range
Range("CF" & .Offset(1, 0).SpecialCells(xlCellTypeVisible_
(1).Row).Select
End With
If ActiveCell.Offset(0, 1) = "2" Then
ActiveCell.Offset(0, -80).Select
ActiveCell.Value = "1 2 3"
End If
End Sub
you can avoid looping through filtered cells by applying another filter on column CG
Sub CorrectMealSwap()
With ActiveSheet
With .Range("CG1", .Cells(.Rows.Count, "CF").End(xlUp)) 'reference referenced sheet columns CF:CG cells from row 1 (header) down to last not empty row of column CF
.AutoFilter Field:=1, Criteria1:="No" 'filter on referenced range 1st column with "No"
.AutoFilter Field:=2, Criteria1:="2" 'filter on referenced range 2nd column with "2"
If Application.WorksheetFunction.Subtotal(103, .Columns(1)) > 1 Then .Resize(.Rows.Count - 1, 1).Offset(1, -80).SpecialCells(xlCellTypeVisible).Value = "1 2 3" ' if any filtered cell other than header then write "a 2 3" in corresponding column D cells
End With
.AutoFilterMode = False
End With
End Sub
My data looks like this.
I would like to delete rows where the value in column named Position from first has value 1. I wrote a macro to do that which is like this
Sub DEL_row()
row_number = 2
Do
DoEvents
row_number = row_number + 1
Position_from_first = Sheet8.Range("E" & row_number)
If InStr(Position_from_first, "1") >= 1 Then
Sheet8.Rows(row_number & ":" & row_number).Delete
End If
Loop Until Position_from_first = ""
MsgBox "completed"
End Sub
But when i run it, I get an error. The msg box says completed but it does not do the required job when I go through the excel sheet.
Can anyone help me what the problem would be.
Thanks a lot for helping...
While your concept is spot on, there are some errors in your syntax and your understanding of VBA. I have listed them below.
No need for Do Events in this code at all.
When deleting rows, you need to start from the last row and step back to the beginning in your looping because as you soon as you delete a row it affects all the row references below that, so your loop count will miss rows if you go forward.
You also need to use .EntireRow.Delete. .Delete will only delete cell content.
I have fixed your code below to reflect these issues. I have also provided a simpler alternative to solve your problem below that which uses the AutoFilter method.
Sub DEL_row()
'get last used row in column E
row_number = Sheet8.Range("E" & Sheet8.Rows.Count).End(xlup).Row
For x = row_number to 2 Step -1
Position_from_first = Sheet8.Range("E" & row_number)
If InStr(Position_from_first, "1") >= 1 Then
'If Sheet8.Range("E" & row_number) = 1 ' this will also work
Sheet8.Rows(row_number & ":" & row_number).EntireRow.Delete
End If
Next
MsgBox "completed"
End Sub
You can avoid the loop altogether (and save time) if you use AutoFilter, like this:
Sub DEL_row()
With Sheet8
'get last used row in column E
row_number = .Range("E" & .Rows.Count).End(xlup).Row
With .Range("E1:E" & row_number)
.AutoFilter 1, "1"
.Offset(1).SpecialCells(xlCellTypeVisible).EntireRow.Delete
End With
.AutoFilterMode = False
End With
Msgbox "completed"
End Sub
I gather that you do not want to delete 11, 12, 31, ... etc. so using the InStr function is not an appropriate method of identifying the correct rows to delete. Your sample data image shows column E as having true, right-aligned numbers and the Range.Value2 property can be directly compared against a 1 to determine if the row should be removed.
As mentioned, working from the bottom to the top is important when deleting rows. If you work from the top to the bottom you risk skipping a row when a row has been deleted, everything shifts up and then you increment to the next row.
Option Explicit
Sub DEL_1_rows()
'declare your variables
Dim rw As Long
'this will greatly improve the speed involving row deletion
Application.ScreenUpdating = False
With sheet8 '<~~ this is a worksheet CodeName - see below
For rw = .Cells(Rows.Count, 5).End(xlUp).Row To 2 Step -1
If .Cells(rw, 5).Value2 = 1 Then .Rows(rw).EntireRow.Delete
Next rw
End With
Application.ScreenUpdating = True
MsgBox "completed"
End Sub
An AutoFilter Method can speed up identifying the rows to delete. However, a single large bulk deletion can still take a significant amount of processing time.
Option Explicit
Sub DEL_Filtered_rows()
'this will greatly improve the speed involving row deletion
Application.ScreenUpdating = False
With Sheet8 '<~~ this is a worksheet CodeName - see below
If .AutoFilterMode Then .AutoFilterMode = False
With .Range(.Cells(1, 5), .Cells(Rows.Count, 5).End(xlUp))
.AutoFilter field:=1, Criteria1:=1
With .Resize(.Rows.Count, .Columns.Count).Offset(1, 0)
If CBool(Application.Subtotal(103, .Cells)) Then
.SpecialCells(xlCellTypeVisible).EntireRow.Delete
End If
End With
.AutoFilter
End With
End With
Application.ScreenUpdating = True
MsgBox "completed"
End Sub
I found it a little odd that you were identifying the worksheet with a Worksheet .CodeName property rather than a Worksheet .Name property. I've included a couple of links to make sure you are using the naming conventions correctly. In any event, I've use a With ... End With statement to avoid repeatedly reidentifying the parent worksheet.
I’ve got the following bit of code that copies rows from one sheet to another if it is the same region that has been selected in a combo box. The problem I have is that it is copying into row 5 rather than row 6 and copying over the column titles. Do you know why this might be happening? I thought the fourth line starting "Worksheets("NLE_").Range("A")... would pick the first empty row i.e. A6, but it picks A5?
If chkNLE.Value = True And cboLA = "All" Then
LastRow = Worksheets("NLE_").Range("A" & Rows.Count).End(xlUp).Row *– clears any data that is already in the worksheet*
Worksheets("NLE_").Range("A6:W" & LastRow).Clear
For Each i In Sheets("NLE").Range("NLEregion") *– NLEregion is a named range for the column with region in it in the sheet when the data is being copied from*
If i.Value = cboRegion.Value Then
i.EntireRow.Copy
Worksheets("NLE_").Range("A" & Rows.Count).End(xlUp).Offset (1)
Sheets("NLE_").Visible = True
Sheets("Front Page").Visible = False
UserForm1.Hide
End If
Next i
End If
Thanks for the suggestion #Siddarth Rout. I went away and worked out how to use Autofilter to do this and it is much quicker than my previous code, which at work was a little slow becasue we are on a thin client.
There maybe a more efficient way of doing this as I am new to VBA but this does work.
If chkNLE.Value = True And cboLA = "All" And cboLA <> "" And cboRegion <> "All" Then
Application.ScreenUpdating = False 'stops the screen from updating while copying and pasting
LR = Sheets("NLE").Range("A" & Rows.Count).End(xlUp).row ' sets LR to the last cell number in A
Set rng = Sheets("NLE").Range("A5:V" & LR) ' Sets rng from A5 to the last cell in column V - includes second row of titles
With rng
.AutoFilter
.AutoFilter Field:=13, Criteria1:=cboRegion.Value 'filters on the region selected in the drop down box
.SpecialCells(xlCellTypeVisible).Copy 'copies just the filtered data
End With
Sheets("NLE_").Range("A5").PasteSpecial xlPasteAll 'pastes just the filtered data into NLE_
Sheets("NLE").AutoFilterMode = False
Application.CutCopyMode = False
Sheets("NLE_").Range("A5:V5").AutoFilter 'Adds the filter back to NLE_
Sheets("NLE").Range("A5:V5").AutoFilter
Application.ScreenUpdating = True 'allows the screen to update