I am very new to VBA and macros in Excel. I have a very large excel spreadsheet in which column A holds dates. I am trying to delete the rows which have only Today's Date and this is what I have come up with till now..
Sub DELETEDATE()
Dim x As Long
For x = 1 To Cells.SpecialCells(xlCellTypeLastCell).Row
Debug.Print Cells(x, "A").Value
If CDate(Cells(x, "A")) < CDate("Now()") Then
Cells(i, "A").EntireRow.Delete
End If
Next x
Next i
End Sub
There are a number of issues with your code. If you are new to VBA then you should put Option Explicit at the start of each Class/Form/Module so that you will get quicker feedback on errors you are making.
You should always qualify excel references by the Worksheet so that you are not working with the implicit active sheet e.g. you need myWb.Range rather than just Range. There are lots of examples if you google this topic.
You can use the macro recorder to record code to see what Excel is expecting in terms of usage. Code produced by the macro recorder is generally not very good code so only use it to clarify, not a style to aspire to.
You are also making a classic mistake when you come to delete a row, which is that you are changing the collection with which you are working.
VBA and Excel(or any other application) are two seperate entities. Neither knows what the other is doing unless they pass messages to each other through a set of well defined interfaces. These interfaces are provided by the application. E.g. you tell Excel you want to work on a range object by using the Range 'interface' etc.
When you look at your for loop you have told VBA that it is going to loop x number of time with x being a value obtained from excel.
VBA is given the final number for x but has absolutely no idea that this value is connected to a set of objects in Excel.
In your for loop you delete one of the rows in Excel. This means that all rows after the row you delete are renumbered to be one less than they were. But VBA doesn't know this, it just knows it is increasing x by 1 until it reaches the maximum value you set earlier.
The problem is, now that you have deleted a row, that that maximum value no longer represents the number of rows you are processing.
Even more horrible, the row that was e.g. row 4 is now row 3 because you deleted the old row 3, but x, which is currently 3 is going to be increment to 4, but the old row 4 is now row 3 so that x=4 is now looking at the old row 5 which is the new row 4. This means that every time you delete a row, you will skip over the row that was one more than the row you deleted.
Its easy to avoid this problem by counting down rather than counting up. So your for loop should be
For x = Cells.SpecialCells(xlCellTypeLastCell).Row to 1 step -1
You should aslo use variable names that mean something. In this case I'd suggest myRow rather than 'x'.
Two other good practises are
Always do a Debug.Compile project before trying to run your code. This will check the whole of your code for syntax errors.
Install the free and fantastic Rubberduck addin for VBA. You can use this addin after your code compiles cleanly. Use the Code Inspections to find out where you have made assumptions you didn't know you'd made.
Suppose I am having two sheets where on sheet 1 is this kind of data (different length of information within one row):
It might be lot of text that is "wrapped" to fit in a row (decently)
But, when I try to reference the same text, or try to use some formula for instance INDEX/MATCH to get me the same text, I am failing to get proper ROW Height...so I must adjust manually sheet 2 rows to fit nicely from heights...
Is there a way in Excel to automatically make these row expansions? Without taking a manual action every time because I won't be always sure how long it will be my original text...
You could add something into the Worksheet_Change event
Private Sub Worksheet_Change(ByVal Target As Range)
Target.WrapText = True
Target.EntireRow.AutoFit
End Sub
It might get annoying if it runs after every change for every cell, so you should add more if statements to limit which cells trigger the code.
I need to use my active column in my range statements. How to write down this:
Range((Activecolumn)2)
So I need a column value to be dynamic because I am moving left and right by pressing a userform button, but the row number always stays the same. At first I am working with column "C" and select it with
Columns("C").Select
and I navigate the selected column with
Private Sub Next_Format_Button_Click()
ActiveCell.Offset(, 1).EntireColumn.Select
End Sub
So when I navigate to the next column I need all my other range statements to go along with it. So basically the column letter in my range statements should be something like "current" or "active" column.
Could someone, please, help me?
Range(ActiveCell.address).entireColumn.copy
Is what you are looking for I think?
I'd also recommend avoiding ".Select" unless absolutely necessary. It is adding unnecessary lines to your code and leaves the program at greater risk of inadvertent user corruption by selecting another range via mouse before the copy (or whatever) operation completes. Better to go straight to whatever operation you intend to do, or by assigning the range to a variable.
edit: Ah, sorry, I see now your only looking row 2, then its Cells(2, ActiveCell.Column) as pointed out above.
If you want to get the Number of the column where you selected something:
ActiveCell.Column
If you need the letter of the column your selection is in:
Split(ActiveCell.Address, "$")(1)
Many "advanced" (aka: VBA) excel tutorials on the web or even excel's vba help encurage us to use the
Range("B2:B10")
method (to be precise: object) for selecting cells or getting values. In the same place they often add it's totally ok to use predefined names as well:
Range("valuesabove")
On the other hand I fell in love with the incredible power of relatively defined cell names. They make it so much easier to write and handle big composite formulas, and basically to refer to nearly anything.
However, relative names don't work in the Range("valuesabove") method the way we are used to it.
Usually (when used on the worksheet) relative names are relative to the currently selected cell or to the cell in which they are used.
In VBA's Range() object this is not true. Range is relative to a WorkSheet object, by default to the ActiveSheet. But ActiveSheet is represenetd by its leftupper cell, A1. And this is what Range turns out to be relative to. And this is why absolute names ($C$23) do work with it, and relative ones ("one column to the left, two rows up") don't.
So my question is:
How can I harness the power of relative names in VBA then?
EDIT:
Realising that my question was rather unclear (thx's go to you guys commenting tirelessly) let me try to put it in a specific form and clarify terms:
IMHO on an excel worksheet it is very comfortable to use names in order to refer to cells or define calculated values by functions based on cell values.
In excel a reference to a cell can be either relative, absolute, or mixed. This is true also when creating names. Thus we can speak about absolute, relative or mixed names (in terms of referring of course).
Here an absolute name is used a couple times (created using excel's Trace Dependents function):
Name "name" = $D$2
A relative name is used a couple times here:
Name "upright24" while, e.g. cell A7 is selected = C3 (without $ signs!). But this changes constantly according to the selected cell or region. You can check it in the name manager! (Ctrl+F3)
And this is what we can consider as a mixed name:
Name "rel_serialnumber" while, e.g. cell C6 is selected = $B6. The row of which (6) changes constantly according to the selected cell or region.
The creation of a relative or a mixed name is explicitly based on the active cell at the moment of creating the name. The creation of an absolute name naturally doesn't rely on the cursor position.
Note, that
absolute names mean a dinamic offset from the referenced cell, which is one and only
relative names mean a static offset from the referenced cell, which thus changes always corresponding to the place where the name is used
mixed names mean a mixed (or half-dynamic) offset from the referenced cell, the row or column of which thus changes always corresponding to the place where the name is used while the other remains always the same (the offset in one or the other direction remains zero).
Okay, now here is the thing. I have a database-like excel sheet where I handle the rows like records and the columns as fields for properties. The user uses this thing as follows: he "selects a record" by placing the cursor in any cell of the row of the desired record. Then he presses a big command button which starts my VBA macro. This intends to open a prepared skeleton file and fill some specific cells in it (which are btw defined by absolute names) with some values (which are defined by mixed names) from the selected record.
Since Range("name") is considered ok to use in VBA (see above) I thought Range("relativename") or Range("mixedname") will work just as fine while automatically relying on the active cell.
I couldn't be worse.
Only Range("absolutename") works in the way one would expect! Explanation see above.
So I'm after a function / method / object that is possibly as comfortable to use with a "relativename" or a "mixedname" as Range("absolutename") is.
It appears you are looking for Range.Offset() http://msdn.microsoft.com/en-us/library/office/ff840060%28v=office.15%29.aspx
However you could do it as:
'Your example Range(Col_B_in_current_row) as
Range("B" & ActiveCell.Row).Select
'Your example Range("B2:B10") -> Range("valuesabove") as
Range("B2:B10").Offset(-1, 0).Select
Just seems like a relatively simple syntax already exists for this.
I think I've found a proper and compact solution. It's
Names("mixedname").RefersToRange
Not as short as Range("mixedname") would be but it is really providing the expected values.
UPDATE:
This solution is mostly unuseful if you want to copy relative-named cell values in a source workbook to relative-named cells in a dest workbook with a single codeline. This is because Names() relies on the actual position of the cursor which is depending on which workbook is currently the active one and in most cases this won't be ok for the other.
In this case the non-fixed part of the name has to be stored:
sourcerow = ActiveCell.Row
[...]
'opening a wbk, this also makes it the active one
[...]
Names("dest").RefersToRange = mysheet.Cells(sourcerow, mybook.Names("src").RefersToRange.Column)
To reference a Range relative to another Range you can use this syntax:
myRange.Range("namedRange")
Note: This only works if both the Row offset AND the Column offsets are positive. For example if the "Refers to" formula for the named range is "=Offset(A1,r,c)", then the above syntax will throw an error if Either r Or c is negative. But, it will work if both are positive.
The asymmetry is unfortunate but business as usual for VBA...
To Reference the third column in the row of the current ActiveCell:
ActiveCell.EntireRow.Range("C1")
To reference a cell offset by (for example) 1 row and 3 columns relative to the ActiveCell:
ActiveCell.Range("C2")
Obviously, you can use the same syntax with the Selection Object or any other Range value in VBA.
Private Sub Worksheet_Change(ByVal Target as Range)
If Not Intersect(Target.Address,ThisWorkbook.Sheets('sheetname).Range('RangeName)) Is Nothing Then _
'Do whatever you want down here.
ThisWorbook.Sheets('sheetname).Range('RangeName).Offset(0,Target.Row)
End If
End Sub
This should send you on the right path to what you want (which is super unclear). Use the worksheet change event to bring in user worksheet selections and changes into VBA modules. Put it into the relevant sheet.
I had the same problem, but I did get it to work - sort of. I don't know what is different about the simple example below, but it works. At first I thought selection mattered, but no - it works without changing the active cell.
(I still can't get it to work in my main spreadsheet.)
Named range: "TestName" = Sheet1!$H1
Values in H1:H10 = 1,2,3,4,5,6,7,8,9,10
Sub Test()
Dim x As Integer
For x = 0 To 10
Range("A1").Offset(x, 0).Value = Range("A1").Offset(x, 0).Range("Testname").Value
Next x
End Sub
Result: A1:A10 = 1,2,3,4,5,6,7,8,9,10
Respected Sir/Madam
I want to key 60 records in first sheet, and 61th records key on next sheet. I want after
60 records cursur is go to automaticully 61th records.
means after every 60th records curuser is going to next sheet.
Thankyou
From your question I think you need to use the SelectionChange event to detect when a cell on any row >60 is selected.
Private Sub Worksheet_SelectionChange(ByVal target As Range)
If target.Row > 60 Then
Sheets(ActiveSheet.Index + 1).Activate
ActiveSheet.Range("A1").Activate
End If
End Sub
This code would need to be placed in each worksheet code module that you wanted the automatic jump to the next worksheet to happen in. It makes the following assumptions:
It fires any time a cell with row number >60 is selected
It just increments the worksheet index to get the next sheet
It assumes your data starts in cell A1
This should allow you to get started though.
With some presto code I would say the logic would be something like this
If ActiveSheet=”Sheet1” AND ActiveRow>=61 then
ActiveSheet=”Sheet2”
End if
I’m sorry that I’m not at a computer with office installed so cant generate the actual code but I think you see where I’m going with this