hopefully you can help me with this!
I am writing a code in VBA and I am having issues with the Selection object acting differently than I expect.
When I write a for loop as in
For Each Cell in Selection
MsgBox Cell.Value
Next Cell
It works as expected, but then I try to index it and it acts differently. Especially it is a non-contiguous cell selection.
Like this;
For i = 0 to 5
MsgBox Selection(i).Value
Next i
It gives pretty random values. any insight would be great!
Edit:
Thanks for the input everyone, it seems I need to find another way of doing the following. I have a piece of code that takes a user's selected cells and uses those values for calculations. Right now, I have been trying to make it so they can select non-contiguous cells. Basically, I need to make an array of these values, and my thoughts were to make a for loop as follows
For I = 0 To 5
Array(i) = Selection(i).Value
Next I
I'm not sure if there is another way of doing this. If anyone has some suggestions, I am interested!
As others have stated, you should not use Selection this way. But to answer your question...I did some quick testing based on your example use-case.
It appears that when you loop cell in Selection it goes left to right, then top to bottom by range area. So for example if you have two non-contiguous ranges selected then if will look something like this:
When you index it, it does not seem to make the jump between range areas. instead it will continue iterating left to right top to bottom within the column bounds of the first range area:
So if you slected 2 columns, iterating on the index would just continue down the first column, even if it is outside of the selection.
My test code:
Private Sub testing()
Dim i As Integer
For i = 1 To Selection.Cells.Count
Selection(i).Value = i
Next i
End Sub
Private Sub testing2()
Dim c As Range
For Each c In Selection.Cells
c.Value = c.Column
Next c
End Sub
Related
After several hours of research, I still can't solve what seems to be a pretty simple issue. I'm new to VBA, so I will be as specific as possible in my question.
I'm working with a DDE link to get stock quotes. I have managed to work out most of the table, but I need a VBA to create a finished formula (i.e., without cell referencing) in order to the DDE link to work properly.
My first code is as follows:
Sub Create_Formulas()
Range("J1").Formula = "=Trade|Strike!" & Range("A1").Value
End Sub
Where J2 is the blank cell and A2 contains the stock ticker. It works fine, but when I try to fill out the rows 2 and bellow, it still uses A1 as a static value.
Sub Create_Formulas()
Dim test As Variant
ticker = Range("A1").Value
'Test to make variable change with each row
'Range("J1:J35").Formula = "=Trade|Strike!" & Range("A1:A35").Value
'not working
Range("J1:J35").Formula = "=Trade|Strike!" & ticker
'not working
End Sub
I couldn't find a way to solve that, and now I'm out of search queries to use, so I'm only opening a new topic after running out of ways to sort it by myself. Sorry if it is too simple.
You are referencing absolute cell adresses here. Like you would do when using $A$1 in a normal excel formula.
What you want to do is:
Dim row as Integer
For row = 1 to 35
Cells(row,10).Formula = "=Trade|Strike!" & Cells(row,1).Value
Next row
This will fill the range J1 to J35 with the formula. Since (row,10) indicates the intersection of row and column 10 (J)
Firstly, in your second set of code, you define a variable "test", but never give it a value.
You assign a value to the variable "ticker", and then never reference it.
Secondly, the value you have assigned to ticker is a static value, and will not change when it is entered in a different row.
Thirdly, I think your issue could be solved with a formula in Excel rather than VBA.
The "INDIRECT" function can be quite useful in situations like this.
Try inserting the formula
=INDIRECT("'Trade|Strike'!"&A1)
into cell A1, then copy down.
Note the ' ' marks around "Trade|Strike". This is Excels syntax for referencing other sheets.
I'm using the code shown below to essentially make the first row of the spreadsheet color grey based on if the cell is empty or not. For example Cell A1 is not blank so color it grey, cell B1 is not blank so color it Grey but cell C1 is blank so don't color it at all. So far my script colors the whole row grey based on A1. Is there an alternative for this?
Sub test()
Dim c As Integer
c = Application.WorksheetFunction.CountA(ActiveSheet.Range("A1"))
If c > 0 Then
ActiveSheet.Range("A1").EntireRow.Interior.ColorIndex = 48
End If
End Sub
When working with Excel-VBA, it's enourmously helpful to grasp the idea of Objects and Collections and how to loop them.
The Watch-window is your best friend here, as is the With keyword when writing code.
In your case:
a Cell is an object inside the Worksheet.Cells-collection (in your case, also an object inside the ActiveSheet.Rows(1).Cells-collection.
This does what you want, based on the information you gave us (you didnt specify that you do NOT want to loop the whole row ;) )
Option Explicit
Sub ColorCells()
Dim objCell As Object
With ActiveSheet
With .Rows(1)
For Each objCell In .Cells
With objCell
If .Value > 0 Then .Interior.ColorIndex = 48
End With
Next objCell
End With
End With
End Sub
This should give you an idea how Excel-Objects work.
Obviously, we can write this shorter:
Sub ColorCells()
Dim objCell As Object
For Each objCell In ActiveSheet.Rows(1).Cells
If objCell.Value > 0 Then objCell.Interior.ColorIndex = 48
Next objCell
End Sub
Note:
This code will loop all 16k Cells in your Row. Obviously, we could stop at your last used Cell. However, since this runs in under a second, i left that out on purpose to keep the code clean
While you could do this with conditional formatting, i support the idea of doing this with code ONCE, with no traces (that is, your conditional formats), left.
I understand what you tried to do with .CountA and .EntireRow, this doesnt work here.
I have a data document i'm working on but some things on it are wrong so i need to overwrite specific cells automatically. I'm looking to set up a loop that goes through each cell in column G and if a certain value is present then insert a formula into the cell in the same row but in column J. I've got as far as setting up the loop but I don't know how to put the formula in the new cell and relate it to the current cell. I was thinking R[]C[] formulas may be the way to go but think this becomes confusing when the formula in not straightforward e.g VLOOKUPS with IFs and MATCHES etc...
Sub FindDefects()
Dim RngCl As Range
Dim Rngg As Range
Set Rngg = Sheet1.Range("A1:A6")
For Each RngCl In Rngg.Cells
If RngCl.Value = "TEXT" Then
RngCl.Offset(0,3).FormulaR1C1 = "R[0]C[-1]/R[0]C[3]"
Else
'Nada
End If
Next RngCl
End Sub
I'm not sure really where to go from here, especially how to add in formulas such as:
=IF(LEN(J9)>0,J9*VLOOKUP(M9,Core!A:C,3,FALSE)/VLOOKUP(K9,Core!A:C,3,FALSE),P9)
Instead of R1C1 formulas. Any help on moving forward is appreciated!
I've come across Stackoverflow many times when I've been looking for excel VBA scripting answers, but I've come up to a wall with my searching for an answer to my problem.
I have data which I need to sort, but it needs to be row grouped first, otherwise the sorting takes away the conformity of the info.
I want to group using info on just 1 column, and have found VBA scripts which do what I want except that they group 1 row to many. (if that makes sense)
i have info a little like
a b c d
Revise blank cell info info
blank cell blank cell info info
blank cell blank cell blank cell blank cell
Revise blank cell info info
blank cell blank cell info blank cell
Revise blank cell info info
etc etc
I want to group the top 3 rows, then the next 2.
but the only VBA script I found looks down the column for the word 'revise' but then groups the cells above the word revise, not below
I hope all that makes sense
thanks for any help
BTW, I dont really have any knowledge of programming, other than what I've gained through running some macros on other projects. Hopefully I wont need telling like a 5 YO, but may need some explanations of any code specific terms
I got this VBA script which works as I described
Option Explicit
Sub Macro2()
Dim rData, rCel As Range
Set rData = Range("a1", Range("a" & Rows.Count).End(xlUp))
Application.ScreenUpdating = False
With rData
On Error Resume Next
.Rows.Ungroup
.Rows.EntireRow.Hidden = False
On Error Goto 0
End With
For Each rCel In rData
If rCel = "END" Then Exit For
If rCel <> "Revise" Then
Rows(rCel.Row).Group
rCel.EntireRow.Hidden = True
End If
Next
Application.ScreenUpdating = True
End Sub
Here's a quick and dirty non-VBA solution, as you say you are not overly familiar with VBA.
Add a column to the right of your data, which will hold a new index that tracks each time 'Revise' is listed in column A. Starting in A2 and copied down, this will look as follows [you may need to hardcode the first entry as 1]:
=if(A2="Revise",A1+1,A1)
This will create a column which increases by 1 each time there is a new "Revise". Then just select your entire data block, right-click, and Sort by your new index column.
I am relatively new to VBA and I need help with this please.
I have a private sub within a sheet and I want it to autofill formulas adjacent to a dynamic named range, if the size of the range changes.
(edit) I am pasting data from another worksheet into this one columns A-M. My dynamic range is defined as =OFFSET($A$1,1,0,COUNTA($A:$A)-1,13). The first If statement should exit the sub if there is no data in column M and I had the destination calculating the last row of column M because I want to fill the formulas in N:O so that they cover the same number of rows as column M.
This is my code and it works if the size of the range gets smaller (i.e. if I delete rows from the bottom), but not if it gets bigger and I can't work out why!
Private Sub Worksheet_Change(ByVal Target As Range)
If Me.Range("M2").Value = "" Then
MsgBox "No Data!"
Exit Sub
Else
If Intersect(Target, Me.Range("rngOracleInvoices")) Is Nothing Then
Application.EnableEvents = False
Dim Lrows As Long
Lrows = Me.Cells(Me.Rows.Count, "N").End(xlUp).Row
Me.Range(Me.Cells(3, 14), Me.Cells(Lrows, "O")).ClearContents
Me.Range("N2:O2").AutoFill Destination:=Me.Range("N2:O" & Me.Range("M" & Me.Rows.Count).End(xlUp).Row)
End If
End If
Application.EnableEvents = True
End Sub
I put the last bit into a separate macro to test if it works on its own and for some reason, when I run it, the autofill goes all the way up to row 1 and overwrites the formulas, which is weird because I use that code a lot and it's never done that before. What have I done??!!
Also, if there is a better way to do the autofill I'd appreciate if someone could let me know what it is because I just cobbled that together from bits I found on forums :)
Thanks,
Soph
In this line Me.Range("N2:O2").AutoFill Destination:=Me.Range("N2:O" & Me.Range("M" & Me.Rows.Count).End(xlUp).Row) you calculate your last row on the column M so if it is empty it'll give you 1 and autofill your formula on row 1.
So start by calculating it on the good column (my guess is O)
You can also simply define an Integer variable to test it and if it is inferior to 2, change it back to 2, 3, 4 or whatever you want.
For your dynamic range, we might need some precision.
And for the AutoFill, you could just select manually the range N2:02 and then double-click on the bottom right square (the one you drag to autofill) and it'll autofill as long as there data in adjacent cells! (give it a try ;) )