0

Original Post

I am fairly new to VBA and until now I managed to do everything. I am stuck at a "basic-looking" task, which is to copy a row from a sheet to another sheet, based by the background color of the cells. The background is done by conditional formatting and are only red if the values are out of tolerance.

I tried multiple things, couldn't do it with anything yet. Not even close.

My last try was with Cells.DisplayFormat.Interior.Color = vbRed, but I couldn't manage the searching row-by-row. I also tried using for the For cycle AllRows = ActiveSheet.UsedRange.Rows.Count so that it only goes to the last used row, but I got stuck pretty quickly.

If you can help me with this, I would appreciate it very much. If I missed any crucial part of the problem, please ask, I will gladly provide it.

Thanks!

Solution

    Sub copy_formatted()
    
    Dim startRange As Range
    Dim copyRange As Range
    Dim i As Long
    Dim startOfRowCell As Range
    Dim Cell As Range
    
    ' edit to the top left cell of the data area
    Set startRange = Worksheets(2).Range("A5")
    ' edit to the top left cell of the area where you want to copy the rows
    Set copyRange = Worksheets(1).Range("A5")
    ' variable for next empty row
    i = 0
    
    ' Loop over all rows
    For Each startOfRowCell In Worksheets(2).Range(startRange, startRange.End(xlDown))
    
        ' loop over all cells in row
        For Each Cell In Worksheets(2).Range(startOfRowCell, startOfRowCell.End(xlToRight))
    
            ' check for formatting
            If Cell.DisplayFormat.Interior.Color = vbRed Then
                ' copy whole row
                Cell.EntireRow.copy
                ' paste to second table (only values, not the formatting)
                copyRange.Offset(i, 0).PasteSpecial xlValues
                ' increment to paste next row beneath
                i = i + 1
                ' break the inner loop to check next row if "red" value is found
                Exit For
            End If
        
        Next Cell

Next startOfRowCell

End Sub
  • If you provide the source of the data and the expected result the answer will faster and more probable. – Black cat Aug 14 '23 at 13:35
  • If the fist cell in the row is specifically colored (red), isn't it enough to be checked? If so, you should iterate as `For i = 2 to AllRows` `If Range("A" & i).DisplayFormat.Interior.Color = vbRed then` 'do whatever you need `End If` `Next i`. – FaneDuru Aug 14 '23 at 13:51
  • My source of data is from a .csv file, that this macro formats to a more human-friendly format. I can provide the sheet that i already have, maybe that way it is easier to understand my problem. Thanks. – BenedekDani Aug 16 '23 at 05:29

2 Answers2

0

EDIT

For multiple columns of conditionally formatted cells I recommend a nested loop like this:

Sub copy_formatted()

' edit to the top left cell of the data area
startRange = Table1.Range("A1")
' edit to the top left cell of the area where you want to copy the rows
copyRange = Table2.Range("A1")
' variable for next empty row
i = 0

' Loop over all rows
For Each startOfRowCell In Table1.Range(startRange, startRange.End(xlDown))

    ' loop over all cells in row
    For Each cell in Table1.Range(startOfRowCell, startOfRowCell.End(xlToRight))

        ' check for formatting
        If cell.DisplayFormat.Interior.Color = vbRed Then
            ' copy whole row
            cell.EntireRow.copy
            ' paste to second table (only values, not the formatting)
            copyRange.Offset(i, 0).PasteSpecial xlValues
            ' increment to paste next row beneath
            i = i + 1
            ' break the inner loop to check next row if "red" value is found
            Exit For
        End If
    
    Next cell

Next startOfRowCell

End Sub

ORIGINAL

Since you did not provide the structure of your data I asumed something like this in the first sheet with the conditional formatting in the second column (in my case named Tabelle1 and starting in the top left of the sheet):

a   15  jan
b   53  feb
a   514 mrz
...

and a second empty sheet called Tabelle2.

Now you have to iterate over the cells with the relevant formating (in my case the second column) and simply copy the whole row to the second table like so:

Sub copy()

i = 0

' Loop over all cells in the respective column
For Each cell In Tabelle1.Range(Tabelle1.Cells(2, 2), Tabelle1.Cells(2, 2).End(xlDown))

    ' check for formatting
    If cell.DisplayFormat.Interior.Color = vbRed Then
        ' copy whole row
        cell.EntireRow.copy
        ' paste to second table starting in the top left (only values, not the formatting)
        Tabelle2.Cells(1, 1).Offset(i, 0).PasteSpecial xlValues
        ' increment to paste next row beneath
        i = i + 1
    End If
    
Next cell

End Sub
Jan_B
  • 98
  • 8
  • So i have 12 parameters and about 6-8k rows of measured data. These parameters have tolerances and basically all of the measurements get conditionally formatted based on these values. I don't really have just 1 column of conditionally formatted or red cells, it can be almost anywhere. I dont really know how to "upload" the workbook, and my code wont show you anything i think. – BenedekDani Aug 16 '23 at 05:47
  • I tried your code but couldn't make it work. I tried adapting it but for the "cell" variable I was unable to give values. It gives an error: Run-time error '424': Object required. – BenedekDani Aug 16 '23 at 06:07
  • @BenedekDani If you are talking about the `cell` from the `For Each` loop: you can adapt the part after the `In` to represent the whole area where the formatted values are. But since your Data seems large this might take some time. Did I understand correctly that there are multiple columns and as soon as one of them has a value out of range the whole row should be copied? – Jan_B Aug 16 '23 at 06:47
  • Yes, so if a row (a row is one measured data-pool), has any out-of-tolerance measurements, it should be copied to another sheet, where i want to further analyse the data. I dont care about (yet) which column was red. – BenedekDani Aug 16 '23 at 09:22
  • @BenedekDani ok, is it also possible for more than one value per row to be "red"? If so, I would suggest a nested for loop to first iterate over all rows and inside to iterate over each cell of the row. This way you can skip the inside loop when one "red" is found so you do not copy the row twice. – Jan_B Aug 16 '23 at 09:47
  • Yes, it is possible, that more than one cell is red in one row. @Notus Panda suggested (and you also said, that checking the interior color would take a long time), that checking the actual value of the cells in an array would be much faster. I am currently working on this method, but I am not familiar with arrays yet. – BenedekDani Aug 16 '23 at 09:57
  • @BenedekDani I edited my answer to show the "slow" solution. Performance is only important if you do not have the time to wait or if it is often repeated and waiting hurts your process. Otherwise it is better to have working "ugly" code than nothing. If you have time on your hand you can improve always later whilst you have a working solution at hand ;) – Jan_B Aug 16 '23 at 10:16
0

Conditional formatting might be a bit diferent color-wise than what you'd expect from it. Here's an example to show what I mean:

enter image description here

and here the code you can use to make this happen if you'd conditionally format 1 to 10 (conditional formatting, color-scaling red-yellow-green):

Sub test()
    Dim arr, lRow As Long, ws As Worksheet
    Set ws = ThisWorkbook.Sheets(1) 'adjust this to your need
    lRow = ws.Range("A" & Rows.Count).End(xlUp).Row 'check what the last used row is
    Dim i As Long
    ReDim arr(1 To lRow, 1 To 1) '
    For i = 1 To lRow
        arr(i, 1) = ws.Range("A" & i).DisplayFormat.Interior.Color 'check the color value
    Next i
    ws.Range("B1").Resize(lRow).Value = arr 'displaying what the color values are of a conditional formatted color-scaling
    ws.Range("A" & lRow).Offset(1).Interior.Color = vbRed 'now THIS is pod- ... this is vbRed
    With ws.Range("B" & lRow).Offset(1)
        .Value = .Offset(, -1).Interior.Color 'vbRed is way... way down to the values you'd normally get from conditional formatting
    End With
End Sub

So unless you're sure your conditional formatting is setting it to that value (also see this answer for more in-depth answering about the different values), it's better to have a different approach. Which red would you draw the line in my example even?
If you're setting it to that specific red however with a "$A2 > 50" or something similar, it'd be easier to just check if the value exceeds your limits.

Notus_Panda
  • 1,402
  • 1
  • 3
  • 12
  • I already have gone through that answer, i think i saw every post on every site with the various answers to this problem. I checked the value of the red cells, and it is RGB 255,0,0. I don't have color-scaling, it gets only red if the value is out of tolerance. – BenedekDani Aug 16 '23 at 05:52
  • Like I said, if you have a rule set up with a specific "out of tolerance" bound, just check for that value if the interior color doesn't work out but it technically should be able to checked on color if it's only that specific red. From reading the comments on the other answer, checking the background color of that many cells would (like Jan_B said) take quite a while. How you would do it, is by going row by row and column by column with 2 for loops, if one cell is red => copy row and exit the most inner for-loop (column). – Notus_Panda Aug 16 '23 at 07:00
  • It might be possible.. so basically i dont care about the background color, but the actual value of the cell. You are saying, that it should be faster that way? I already have a tolerances module, where i calculate the tolerances for each reference. – BenedekDani Aug 16 '23 at 09:25
  • To check the background color of each cell in a (big) range will take way longer than having vba check the values in an array (so it doesn't interact with Excel all the time) for your tolerance levels. For background color vba needs to interact with Excel for every cell, for the calculation, you only need to shove the rng.Value into an array and have an additional array for the "to be copied rows (not actually rows but all the values necessary in the row)" and that's only 2 interactions with excel when you take in account pasting that secondary array. – Notus_Panda Aug 16 '23 at 09:32