I've been strugling to color alternate rows in a range in VBA. The problem is that the Range method seems not to select the proper range and I end up coloring adjacent cells. This is my desired output:
But, this is what I actually get:
This is the code I created:
Sub limpar_aniversariantes()
Worksheets("Aniversariantes").Range("B4:D900").ClearContents
'Worksheets("Aniversariantes").Range("B4:D900").Interior.Color = RGB(255, 255, 255)
End Sub
Sub gerar_lista_aniversariantes()
limpar_aniversariantes
Dim newrange As Range, rw As Range
Sheets("Base de Alunos").Select
Set newrange = ActiveSheet.AutoFilter.Range.SpecialCells(xlCellTypeVisible)
CountInicioMes = 12
CountInicio = 16
Count = CountInicio
count_first_line = 0
For Each rw In newrange.Rows
Worksheets("Aniversariantes").Range("B" & Count).Value = rw.Cells(2).Value
Worksheets("Aniversariantes").Range("C" & Count).Value = Left(rw.Cells(5).Value, 2)
Worksheets("Aniversariantes").Range("C" & Count).NumberFormat = "DD"
Worksheets("Aniversariantes").Range("D" & Count).Value = UCase(rw.Cells(15).Value)
'Worksheets("Aniversariantes").Range("A" & Count).Value = Count
'pintar linhas alternadamente de cinza
If Count Mod 2 = 0 Then
Worksheets("Aniversariantes").Range("B" & Count & ":D" & Count).Interior.Color = RGB(211, 211, 211)
Debug.Print ("B" & Count & ":D" & Count)
End If
'pegar mes dos aniversariantes, eh preciso pular header
If count_first_line < 2 Then
count_first_line = count_first_line + 1
If count_first_line = 2 Then
my_date = rw.Cells(5).Value
End If
End If
'Debug.Print Left(rw.Cells(5).Value, 2)
Count = Count + 1
'Debug.Print rw.Cells(2).Value
Next rw
Worksheets("Aniversariantes").Range("B" & (CountInicio + 1) & ":D" & Count).Sort key1:=Worksheets("Aniversariantes").Range("C" & (CountInicio + 1) & ":C" & Count), _
Header:=xlNo
'limpar bordas anteriores
Worksheets("Aniversariantes").Columns("B:D").Borders.LineStyle = xlNone
Worksheets("Aniversariantes").Range("B" & (CountInicio) & ":D" & (Count - 1)).Borders.LineStyle = xlContinuous
'.Weight = xlThin.ColorIndex = 3
my_month = Mid(my_date, 4, 2)
my_month_written = RetornarMes(CInt(my_month))
Worksheets("Aniversariantes").Range("B" & CountInicioMes).Value = UCase(my_month_written)
Worksheets("Aniversariantes").Range("B" & CountInicio).Value = "NOME"
Worksheets("Aniversariantes").Range("C" & CountInicio).Value = "DIA"
Worksheets("Aniversariantes").Range("D" & CountInicio).Value = "MODALIDADE"
'mudar cor de fundo
'Worksheets("Aniversariantes").Range("B" & CountInicio & ":D" & CountInicio).Interior.Color = RGB(255, 255, 0)
'Debug.Print my_month_written
End Sub
I am coping values from one worksheet to another and I am using MOD function to only color even lines. Everytime I copy these values the number of lines can change, that's why I need to do it by VBA. I am not mastered in VBA so any help is appreciated. I have struggled to do it the whole morning.
Edit: I notticed that choosing a row out of the PERSON's data table makes the Range().Interior.Color function works properly, the problem is inside the range.
All right,
Thanks to #Thomas Inzina comment, I realized that the formula "=MOD(ROW(),2)<>0" should be translated to my native excel language, e.i: Portuguese. Then the right translation is "=MOD(LIN();2)<>0". But, still I did not figure out why I was getting that weird behaviour that I showed in my question, instead I used this solution (with the proper formula) to solve my issue:
https://stackoverflow.com/a/15957075/1171721
I am satisfied with that since I got what I wanted, but still if anyone discovers why I got this strange behaviour using the old approach, I will be happy to test the proposed solution for the sake of learning.
Related
I am working on automating a report with VBA, the code is working relatively well except for one of my If statements. In fact it works once every other time, and when it doesn't I get a mismatch error. After trying to resolve this all morning, I think that there may be an issue with the variable types I'm using or the fact that I use a text format at some point.
Here's the part where I am having problems, the error, when it pops up, does so at the ElseIf statement marked below
Dim source As Excel.Workbook 'ARS15.xls
Dim target As Excel.Workbook 'reporting cc
Dim clist As Excel.Workbook 'client list
Dim i As Integer
Dim lastRow As Long
Dim rowz As Long
Dim temp As Range 'payment condition and client codes used for index match
Dim pay As Variant 'payment condition
Dim fact As Variant 'insurance status
'get payment conditions and insurance status
clist.Sheets("ZTRE128C").AutoFilterMode = False
lastRow = clist.Sheets("ZTRE128C").Cells(Rows.Count, "C").End(xlUp).Row
Set temp = clist.Sheets("ZTRE128C").Range("C3:S" & lastRow)
temp.Copy
target.Sheets("Countries").Range("D3").PasteSpecial Paste:=xlPasteValues
target.Sheets("Countries").Range("D3:D" & lastRow) = Application.Trim(target.Sheets("Countries").Range("D3:D" & lastRow).Value)
target.Sheets("Countries").Range("D3:D" & lastRow).NumberFormat = "#"
target.Sheets("Countries").Range("T3:T" & lastRow) = Application.Trim(target.Sheets("Countries").Range("T3:T" & lastRow).Value)
For i = 0 To rowz - 1
pay = Application.Index(target.Sheets("Countries").Range("G3:G" & lastRow), Application.Match(target.Sheets("TEST").Range("D" & 7 + i), target.Sheets("Countries").Range("D3:D" & lastRow), 0))
fact = Application.Index(target.Sheets("Countries").Range("T3:T" & lastRow), Application.Match(target.Sheets("TEST").Range("D" & 7 + i), target.Sheets("Countries").Range("D3:D" & lastRow), 0))
If IsNumeric(pay) Then
target.Sheets("TEST").Range("F" & 7 + i) = pay
ElseIf Left(pay, Len(pay) - 1) = "0" Then 'HERE IS WERE THE ERROR HAPPENS
target.Sheets("TEST").Range("F" & 7 + i) = 1
Else: target.Sheets("TEST").Range("F" & 7 + i) = Left(pay, Len(pay) - 1)
End If
If IsEmpty(fact) Then
target.Sheets("TEST").Range("G" & 7 + i).Value = ""
Else: target.Sheets("TEST").Range("G" & 7 + i).Value = ChrW(&H2713)
End If
Next i
I also noticed that when the error appears the code goes through the whole For statement just once...
I would also like to say that I am relatively new to coding and VBA, sorry in advance for any abominations that may be written in this code.
I have the following VBA problem. I have a code a which finds a column and then it inserts a worksheets formula in another column. The formula contains a reference to the previously found column.
Dim intBB As Integer
Dim rngBB As Range
Dim controlBB As Integer
intBB = 1
Do While Worksheets("Sheet2").Cells(2, intBB) <> ""
If Worksheets("Sheet2").Cells(2, intBB).Value = "BbCode" Then
With Worksheets("Sheet2")
Set rngBB = .Range(.Cells(2, intBB), .Cells(2, intBB))
controlBB = intBB
End With
Exit Do
End If
intBB = intBB + 1
Loop
Worksheets("Sheet2").Range("W3:W2500").Formula = "= _
IF (controlBB="""","""",BDP(controlBB&"" Equity"",""ID_ISIN""))"
However, this does not work. How can I correctly refer to the found column?
Since you have a column number, rather than letter, I'd suggest using R1C1 format:
Worksheets("Sheet2").Range("W3:W2500").FormulaR1C1 = "= _
IF (RC" & controlBB & "="""","""",BDP(RC" & controlBB & "&"" Equity"",""ID_ISIN""))"
This is how I would do it
For i = 3 To 2500
Worksheets("Sheet2").Range("a" & i).Formula = "=IF(" & Cells(i, controlBB).Address & "="""","""",BDP(" & Cells(i, controlBB).Address & " & "" Equity"",""ID_ISIN""))"
Next i
I am working with this macro that will look at a block of transactions, insert 3 rows between months, and then add the month and subtotal. The issue is that the break and totals are getting inserted at the beginning of the month instead of the end.
I have tried to adjust the shift but it either ends up giving me an error or the total ends up overriding an existing cell instead of going into a new row. This is a more complex macro than I have worked with before and I'm a little lost now, still working on learning VBA.
Option Explicit
Sub AddAndSum()
On Error GoTo lblError
Application.DisplayAlerts = False
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Dim shData As Worksheet, wbData As Workbook
Dim fr As Long, lr As Long, i As Long, lr2 As Long
Dim intMonth As Long, intYear As Long
Set wbData = ThisWorkbook
Set shData = wbData.Sheets("Sheet1")
fr = 13
lr = shData.Rows.Count
For i = fr To lr
With shData
If (IsDate(.Cells(i, 3).Value) And IsDate(.Cells(i - 1, 3).Value) And Month(.Cells(i, 3).Value) <> Month(.Cells(i - 1, 3).Value)) Or i = fr Then
intMonth = Month(.Cells(i, 3).Value)
intYear = Year(.Cells(i, 3).Value)
.Rows(i & ":" & i + 2).Insert Shift:=xlDown
.Cells(i + 1, 1).Value = "Monthly Total (" & MonthName(intMonth) & ")"
.Cells(i + 1, 2).Formula = "=SUMPRODUCT((MONTH($C$" & fr & ":$C$" & lr & ")=" & intMonth & ")*(YEAR($C$" & fr & ":$C$" & lr & ")=" & intYear & ")*$E$" & fr & ":$E$" & lr & ")"
i = i + 3
End If
End With
Next i
lblError:
If Err.Number <> 0 Then
MsgBox "Error (" & Err.Number & "): " & Err.Description, vbOKOnly + vbCritical
End If
GoTo lblExit
lblExit:
Application.DisplayAlerts = True
Application.ScreenUpdating = True
Application.Calculate
Application.Calculation = xlCalculationAutomatic
Exit Sub
End Sub
This line begins the insertion at Row i.
.Rows(i & ":" & i + 2).Insert Shift:=xlDown
You want to begin the insertion at row i+3, and you can accomplish that with the Offset method:
.Rows(i & ":" & i + 2).Offset(3).Insert Shift:=xlDown
You may also want to see this answer regarding best way of getting the "last row" in a column:
Error in finding last used cell in VBA
As you're currently doing lr = shData.Rows.Count that is 65,336 rows in Excel 2003, or 1,048,576 rows in Excel 2007+ and you almost certainly do not have that many data (otherwise an Insert would fail!), so your loop is cycling needlessly over a bunch of empty rows.
You need to change this row:
intMonth = Month(.Cells(i, 3).Value)
to
intMonth = Month(.Cells(i-1, 3).Value)
At the moment it is setting intMonth to the value of the current cell (which is the first cell of the next month) instead of the value of the previous cell (which contains the month you want to subtotal).
Then add a condition into your loop to add the last subtotal.
Also:
If (IsDate(.Cells(i, 3).Value) And IsDate(.Cells(i - 1, 3).Value) And Month(.Cells(i, 3).Value) <> Month(.Cells(i - 1, 3).Value)) Or i = fr Then
Should this be i = lr ? as you are checking for the last line in the sheet? At the moment will always put a subtotal after the first line. You'll need to update this value when you add the three subtotal lines in as well.
lrow = ws1.Cells(ws1.Rows.Count, 1).End(xlUp).Row
For p = 1 To lrow
period(p) = p
Next p
With ws2
lrow2 = .Cells(.Rows.Count, 1).End(xlUp).Row
.Range("A1").Offset(lrow2, 1).Resize(lrow).Value = Application.Transpose(period)
ws1.Range(ws1.Cells(5, 1), ws1.Cells(lrow, 1)).Copy .Cells(.Rows.Count, "A").End(xlUp).Offset(1, 2)
End With
As you see, I am trying to copy data columns from one sheet to another and it works well. But if notice I am generating a sequence in p from 1 to lastrow , which looks very dumb to me because I am using a loop and I am sure there is another way to generate it and copy it in to another sheet. How can I fasten this as removing the application.transpose(period) line from the code makes it run in half the time. I am requesting for an faster method if anyone can advice on. Thanks.
E.g.
Sheet1 Sheet2
John 1 John
Jim 2 Jim
Jack 3 Jack
I am generating Sheet2 from Sheet1 and the numbers and names are in different columns. I can use copy like I have in my code for names, but I need to generate the numbers myself.
I was curious about this so I measured 4 options:
Max itms: 65,000
Transpose: 0.0586 sec
Formula: 0.0938 sec
Fill down: 0.0273 sec <<<
2D Array: 0.0547 sec
Max itms: 1,000,000
Formula: 0.4688 sec
Fill down: 0.2305 sec <<<
2D Array: 0.6992 sec
.
Test code:
Public Sub idSequence()
Const MAXR As Long = 1000000
Const CRx2 As String = " sec" & vbCrLf ' & vbCrLf
Const NFRM As String = "#,##0.0000"
Dim arr As Variant, i As Long, msg As String, t As Double
If MAXR <= 65000 Then 'Upper Limit: 65,000
t = Timer
ReDim arr(1 To MAXR)
For i = 1 To MAXR
arr(i) = i
Next
Range("A1:A" & MAXR).Formula = Application.Transpose(arr)
msg = msg & "Transp: " & vbTab & Format(Timer - t, NFRM) & CRx2
End If
t = Timer
Range("B1:B" & MAXR).Formula = "=Row()"
msg = msg & "Formula:" & vbTab & Format(Timer - t, NFRM) & CRx2
t = Timer
Range("C1") = 1
Range("C1:C" & MAXR).DataSeries Rowcol:=xlColumns, Type:=xlLinear, Step:=1
msg = msg & "Fill down:" & vbTab & Format(Timer - t, NFRM) & CRx2
t = Timer
ReDim arr(1 To MAXR, 1 To 1)
For i = 1 To MAXR
arr(i, 1) = i
Next
Range("D1:D" & MAXR) = arr
msg = msg & "2D Array:" & vbTab & Format(Timer - t, NFRM) & CRx2
Debug.Print "Max itms: " & vbTab & Format(MAXR, "#,##0")
Debug.Print msg
End Sub
This will output both the columns you are looking for; numbers in one column and the names in the next:
Public Sub YourSolution()
Dim v
v = Sheet1.[CHOOSE({1,2},ROW(OFFSET(A1,,,COUNTA(A:A))),A1:INDEX(A:A,COUNTA(A:A)))]
Sheet2.[b3:c3].Resize(UBound(v)) = v
End Sub
It should be quick enough that you need not bother turning off screen updating or setting calculation to manual.
So your question is how to speed this up? A first suggestion is to add the following to the beginning and end to your Macro:
At the beginning:
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
then at the end:
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
I know there are a ton of questions about constructing looped codes in vBA already but hopefully this will be a quick answer, i wasn't able to find a page addressing this issue.
My goal is to check the values from one range with values in another range, and if is a match it will perform a function and display results at the end. However, if the corresponding value in the range is "N/A" I want the results to display immediately and move onto the next checked value. Right now I am obtaining a 'no for loop' error for my code and i understand why. But I don't know how to fix this problem. Can anyone help?
Sub solubility()
Dim coeff As Range, groups As Range
Dim anion As Range
Dim a As Range
Dim nextrow As Long
Dim j As Range
Worksheets("properties").Select
Range("P7:P2000").Select
Selection.ClearContents
'solubility groups range
groups = Worksheets("Solubility").Range("A2:A33")
'group coefficients range
coeff = Worksheets("Solubility").Range("B2:B33")
anion = Worksheets("properties").Range("AB7:AB887")
For Each a In anion
For Each j In groups
If UCase(a.Value) = UCase(groups(j).Value) Then
If groups(j).Value = "" Or "N/A" Then
Worksheets("properties").Range("P" & a.Row).Value = "N/A"
Next a
Else
anvalue = coeff(j).Value * Range("AC" & a.Row).Value
End If
End If
If UCase(Range("AD" & a.Row).Value) = UCase(groups(j).Value) Then
cavalue = coeff(j).Value * Worksheets("properties").Range("AE" & a.Row).Value
If UCase(Range("AF" & a.Row).Value) = UCase(groups(j).Value) Then
cb1value = coeff(j).Value * Worksheets("properties").Range("AG" & a.Row).Value
End If
If UCase(Range("AH" & a.Row).Value) = UCase(groups(j).Value) Then
cb2value = coeff(j).Value * Worksheets("properties").Range("AI" & a.Row).Value
End If
Next j
If UCase(Range("AD" & a.Row).Value) = UCase("[MIm]") Then
cavalue = Range("AE" & a.Row) * Worksheets("solubility").Range("B2").Value + Range("AE" & a.Row) * Worksheets("solubility").Range("B7").Value
End If
nextrow = Worksheets("properties").Cells(Rows.Count, 15).End(xlUp).Offset(1, 0).Row
Worksheets("properties").Range("P" & nextrow).Value = _
anvalue + cavalue + cb1value + cb2value + Worksheets("solubility").Range("b34").Value
Next a
End Sub
I have the line 'Next a' twice, and excel doesnt like this, but I want to automatically jump to the next checked value without performing the remaining function if I get the "N/A" value.
I know this will rile the feathers of some of my purist brethren, but I would actually suggest a judicious use of GoTo in your case:
For Each a In anion
For Each j In groups
If UCase(a.Value) = UCase(groups(j).Value) Then
If groups(j).Value = "" Or "N/A" Then
Worksheets("properties").Range("P" & a.Row).Value = "N/A"
GoTo NextA
....
End If
End If
....
Next j
....
NextA:
Next a
Overuse of GoTo will quickly turn your code into spaghetti, but in this case I think it is actually the most readable option you have.
You must define a reference to an object using SET:
SET groups = Worksheets("Solubility").Range("A2:A33")
(Same for all ranges)