I am only new to VBA coding so I don't have much of a clue of what I am doing unfortunately. I was hoping that I could get a hand here.
What I want to do is.
Step One: Copy a line (which has formulas) and paste it in the same position but as values only.
Step Two: Move a selection of cells down one line.
Step Three: Copy a line (with formulas) and past it on another line with the formulas.
This is all done on the same sheet.
If I could get some help with this or some direction to some tutorials that would be really appreciated.
Normally I wouldn't do this without your showing some effort, but why not? The below are about as simple as I can get. Please still though, use the macro recorder to see how this all works. It's how a lot of us get started.
Sub StepOne()
Dim myRow As Long
myRow = 1 ' We will "copy/paste" on Row 1
' To avoid using the clipboard, you can simply set two ranges' values equal. This will
' clear out any formulas and leave the values.
Rows(myRow).Value = Rows(myRow).Value
End Sub
And the second:
Sub StepTwo()
Dim rng As Range
'Change this as required. Note the use of `Set` with a `Range` type.
Set rng = Range("A1:A10")
rng.Cut rng.Offset(1, 0)
End Sub
And the last one:
Sub StepThree()
' Not tellin :P! You should be able to get this. Hint: look at StepOne.
End Sub
Edit: Ah, I realize now that Step Three is a little more involved than setting two ranges equal since you want to maintain the formula. However, I'll leave this as a learning opportunity for you to investigate. If you can't figure it out, let me know and I can guide you. :D
Related
I've written about 10 easy macros in the last 18 months, and I would like to understand more than I do in VBA programming.
In none of the subroutines I've written were there arguments in the brackets after the subroutine title.
I have found this code (found here) on stackoverflow.
It's used to find the last row that contains data in the Excel sheet.
But it's got some arguments/parameters (please explain the difference) in the brackets after the subroutine name, which I've never seen before.
What do these arguments stand for? I'm unable to figure this out.
Sub GetLastRow(strSheet, strColumn)
Dim MyRange As Range
Dim lngLastRow As Long
Set MyRange = Worksheets(strSheet).Range(strColum & "1")
lngLastRow = Cells(Rows.Count, MyRange.Column).End(xlUp).Row
End Sub
Another thing I don't understand is why the "MyRange" variable needed to be created.
Was this necessary or is there a way to simplify this code, get rid of "MyRange" variable and use Worksheets("MBank_Statsy").UsedRange property??
Below you can see the worksheet that I would like to apply this code to.
I think the comments upon your question have probably given you all the info you need on why things are done the way they are, but if you're interested, the entire subroutine could be reduced to:
Function GetLastRow(strSheet, strColumn)
GetLastRow = Cells(Rows.Count, Worksheets(strSheet).Range(strColumn & "1").Column).End(xlUp).Row
End Function
Note: this is now a function. To find out the last used cell in Sheet1, column 'C' for instance, you would do something like this:
lastrowused = GetLastRow("Sheet1","C")
I suspect that the original routine started out with more going on (perhaps it found the last cell and did something to it) and has been stripped down to just retrieving the last row but no thought was given to reducing unnecessary instructions.
This is my first post. Forgive me if i am doing something wrong here. I will be glad to correct any mistakes. I have found the web site to be very valuable as i am a baby in the field of vba. Please have patience with me.
I am a super rookie in VBA. I am learning as i go but have spent a lot of time on this. I find bits and pieces of information on the web but have trouble in putting them all together. I have learned how to make a vba macro that i can select a file and then run other macros.
I am using Excel 2013.
I complete a time sheet every week(sometimes more at end of month) of the hours i work and the projects i work on. I also include on that sheet when i am out and a code for the reason. I would like to copy three sections to a summary sheet.
Cell D1. This cell always has the date beside it. I would like to copy this to the cell in my first row.
Cells F3-L3 are cells where a code is put. I would like to copy this to the second cell in my first row.
The next range of cells aret the last cells with data in columns F-L. These vary as we have different numbers of rows for work orders each time but are always in columns F-L. I would like to copy this to a second row below the corresponding cells in the first.
For the next file I would like to copy to the next available row in summary.
I would like to copy this data so i can figure vacation days, sick days, etc.
I know i'm asking alot but would be extremely grateful for any help.
i'm giving you this as Example, you will still need to modify...
Option Explicit 'forces Programmer to declare variables
Sub Button_To_Copy () 'link this to a button or other action that launchs the sub
Dim Range_to_Copy as Range
Dim Range_Destination as Range
Dim Sheet_Data as worksheet 'sheet from where we pull the data
Dim Sheet_Destination as Worksheet' Summary Sheet
set Sheet_Data = Thisworkbook.Sheets("Sheet1") 'you might have to rename the sheetname accordingly to its name.
set Sheet_Destination = Thisworkbook.sheets("Summary") ' wild guess, correct to your summary sheet name
Set Range_to_Copy = Sheet_Data.Range("D1")
Set Range_Destination = Sheet_Destination.range("A1")
Range_to_Copy.Copy Range_Destination 'this copies from range A to B (basically A.copy B), but i changed variable names to make it easier...
'more code, copies
'you can simplify without variables like this:
'Sheets("Sheet1").Range("D1").Copy Sheets("Summary).Range("A1") <===== does the same as the above coding
End Sub
Note that i never used activate or select, wich macro recorder will badly use all the time, making bad habits to starters.
Also, by referencing hard cell location like "D1", the code is not dynamic, if you add more data, the sub will have to be changed, so use this just as a start maybe
I have been trying to select Sheet2 from Sheet1 using a command button click macro. The final part of the macro is designed to find the last used row (offset by 1) and return the row number using MsgBox. I have tried two methods of selecting sheet2. However, in both cases the number returned by MsgBox is the last used row of sheet1, not of sheet2. I know this because when I change the last cell in column A that contains data (i.e. place data in a different cell), the MsgBox provides the new value (offset by 1).
I guess the error may be in my usage of Cells.Find. I know that there are several possible methods of determining the last used row but from what I’ve read the Cells.Find method is the most reliable. The only obvious thing to me is that the row number returned is from Sheet1, not Sheet2. There are no error messages associated with this failure.
The first method is:
Private Sub CommandButton1_Click()
Set wSheet = Worksheets("Sheet2")
wSheet.Activate
With wSheet
unusedRow = Cells.Find("*", [A1], , , xlByRows, xlPrevious).Offset(1, 0).Row
End With
MsgBox (unusedRow)
End Sub
The second method is:
Private Sub CommandButton1_Click()
Application.Goto Reference:=Worksheets("Sheet2").Range("A1"), Scroll:=True
unusedRow = Cells.Find("*", [A1], , , xlByRows, xlPrevious).Offset(1, 0).Row
MsgBox (unusedRow)
End Sub
Can anybody comment on any errors in my code? Any help would be greatly appreciated. I am very much a learner of XL VBA.
Edit: Response to comments
Thanks a lot #Dan; I have read both of those works you referenced. There is another useful discussion I found that I will dig out the link for and put here. I think I'm not yet tuned to the syntax at even a fairly fundamental level, so some of the more subtle variations are still a bit obscure. But I have got the point about avoiding select etc although I can only get there sometimes. I guess that's because I quite often use the macro recorder which tends to use the select family.
Many thanks #Tony - that's the piece of information I needed - the macro works fine now and I understand things a little better. Yes, I did run it with the Activate statement included and it gave me the last used row (offset by 1) for Sheet1; with your revision I get that row number for Sheet2. Thanks again.
I only use VBA occasionally, and every time I come back to it I get caught out by some variation of the following:
I have a Range object, currentCell, that I use to keep track of what cell I'm working with in the spreadsheet. When I update this to point to a different cell, I write:
currentCell = currentCell.Offset(ColumnOffset:=1)
The problem is that I've forgotten the Set keyword, so what the above line actually does is use the default property of the Range objects:
currentCell.Value = currentCell.Offset(ColumnOffset:=1).Value
So the contents of the current cell are overwritten by what's in the new cell, and my currentCell variable hasn't changed to point to a new cell, and I get filled with rage as I realize I've made the same mistake for the hundredth time.
There probably isn't a better answer than to put a post-it on my monitor saying "Have you remembered to use Set today?", but if anyone has any suggestions to help me, I'd appreciate hearing them. In particular:
Is there any way to turn on warnings when you implicitly use default properties? I have never used them like this on purpose, I'd always call Range.Value if that's what I meant.
Is there any good practice for marking variables as "this should only be used to read from the spreadsheet"? In most code I write, almost all my variables are for gathering data, and it would be handy to get a warning if something starts inadvertently editing cells like this.
It took me a while to understand how you are running into problem since I do not think I have ever had this problem in spite of using range objects for years. After thinking about things, I realized I do the following 100% of the time when working with cells - I use the offset or cells functions constantly - I rarely use Set to redefine a current variable.
If I have a loop I am iterating through to go through the spreadsheet, I may do something like
Dim startRng as Range
Set startRng = range("A1")
for i = 1 to 100
startRng.offset(i,0).value = i
startRng.offset(i,1).value = startRng.offset(i,0).value
next i
or
for i = 1 to 100
cells(i,0).value = i
cells(i,1).value = cells(i,0).value
next i
Either of these notations means I almost rarely have to use Set with a range object - almost always this happens once (if at all) and indicates the first cell in a range I will iterate over or reference.
It is also really clear what the offset is - since you specify a row/column - which makes it really straightforward what is happening in the code and easier to track since it references a single cell. You don't have to track down and trace backwards to the last 3 places you update a currentCell Range object.
Adopting a style of coding using these sorts of styles should eliminate nearly all these errors you are making. I am quite serious when I say I cannot remember ever having made a similar error in all my years coding VBA - I use the offset and cells functions continuously in my code (loops in these examples, but I use similar methods with all other examples in code) rather than setting ranges to new ranges. The side effect is that when you are setting a range in your code, it is almost ALWAYS immediately following a Dim statement and much more clear.
Whatever you choose to do, you'll need the post-it note, I'm afraid. After all, setting a range object's value to the value in another cell is a perfectly valid and common thing to do. The code has no way of knowing that you want it to do anything other than what you ask it to.
You could try checking your range object's address before and after you update it to make sure it's different, but if you remember to do that, you would be better off simply using the set keyword to update the object the way you intended.
Now that this issue has enraged you to the point of posting your question, I imagine that you'll never forget it again, regardless of how much time goes by before your next visit to VBA. So maybe you won't need the post-it note after all.
Is there any way to turn on warnings when you implicitly use default properties?
No.
Is there any good practice for marking variables as "this should only be used to read from the spreadsheet"?
Well, you could make your own variable naming convention, à la Making Wrong Code Look Wrong, but you'll still have to check your own code visually and the compiler won't help you do that. So I wouldn't rely on this too much.
A better option is to circumvent the need for repeatedly redifining currentCell using .Offset altogether.
Instead, read the entire range of interest to a Variant array, do your work on that array, and then slap it back onto the sheet when you're done modifying it.
Dim i As Long
Dim j As Long
Dim v As Variant
Dim r As Range
Set r = Range("A1:D5") 'or whatever
v = r.Value 'pull from sheet
For i = 1 To UBound(v, 1)
For j = 1 To UBound(v, 2)
'code to modify or utilise element v(i,j) goes here
Next j
Next i
r.Value = v 'slap v back onto sheet (if you modified it)
Voilà. No use of default properties or anything that could be confused as such. As a bonus, this will speed up your code execution.
There probably isn't a better answer than to put a post-it on my monitor saying "Have you remembered to use Set today?", but if anyone has any suggestions to help me, I'd appreciate hearing them. In particular:
I would slightly change the wording on the Post It
"Are you sure you have not forgotten using Option Explicit and Error Handling?"
Otherwise trust me there is no better way! Having said that, I would like to confirm that "Using Set" is the least of your worries. What should be on the top of your main worries is "Writing Good Code" and this doesn't come overnight. It all comes by practice.
My advice to all beginners. Never assume! For example, .Value is the default property of the range so
Range("A1") = "Blah"
is correct. But still avoid using that.
Always Fully qualify your variables
Always Use Option Explicit
Always Use Error handling
For example, This works.
Option Explicit
Sub Sample()
Dim ws As Worksheet
Dim rng As Range
On Error GoTo Whoa
Set ws = ThisWorkbook.Sheets("Sheet1")
Set rng = ws.Range("A1")
rng.Value = "Blah"
Exit Sub
Whoa:
MsgBox Err.Description
End Sub
Now let's try the above code without using the Set command. Try the below code. What happens?
Option Explicit
Sub Sample()
Dim ws As Worksheet
Dim rng As Range
On Error GoTo Whoa
ws = ThisWorkbook.Sheets("Sheet1")
rng = ws.Range("A1")
rng.Value = "Blah"
Exit Sub
Whoa:
MsgBox Err.Description
End Sub
Recommended Read.
Topic: To ‘Err’ is Human
Link: http://siddharthrout.wordpress.com/2011/08/01/to-err-is-human/
I think enderland has highlighted the basic solution, which is to handle processing multiple cells in a loop. To take it further, I'd suggest using a For Next loop for cycling through cells. Probably on of the most common bits of code I write is something like:
Dim cell as Excel.Range
Dim rngCellsToProcess as Excel.Range
Set rngCellsToProcess = 'whatever you set it to
For each cell in rngCellsToProcess
'do something
Next cell
This won't eliminate the need for Set, but may help remind you to use it, while making it clearer what's going on.
Maybe write your own custom function and use it instead ?
Sub offset_rng(ByRef my_rng As Range, _
Optional row As Integer, Optional col As Integer)
Set my_rng = my_rng.Offset(row, col)
End Sub
Can be used like this:
Sub test()
Dim rng As Range
Set rng = Range("A1")
offset_rng my_rng:=rng, col:=1
rng.Value = "test1"
offset_rng my_rng:=rng, col:=1
rng.Value = "test2"
offset_rng my_rng:=rng, col:=1
rng.Value = "test3"
offset_rng my_rng:=rng, col:=1
rng.Value = "test4"
End Sub
I have scoured the Internet and I have found a handful of possible solutions to this issue, but I wanted to ask here as well.
The goal is to click a button, and spell check an entire sheet.
Here's some code
Sub spellCheck()
Sheet1.Cells.CheckSpelling
End Sub
Also, I found this:
Sub SpellCheck()
Dim Checkword As String, Result As Boolean
Checkword = Selection.Value
Result = Application.CheckSpelling(Checkword)
Selection.Offset(0, 1) = Result
End Sub
Any ideas? Neither is working for me. Thanks!
You can check the whole workbook by doing something like:
Sub SpellCheck()
For Each sh In Worksheets
Sheets(sh.Name).Cells.CheckSpelling
Next
End Sub
this will cycle through each sheet in the entire book and run a spellcheck on each one. What I can't figure out yet is how to make the spell checker actually move to the position of the spelling error. So, with the above you just get a list of spelling errors with no context with which to asses them.
I noticed I just had a typo in my code.
Below works:
Sub spellCheck()
Sheet1.Cells.CheckSpelling
End Sub
But, if anyone knows how to do the entire workbook, I'd be interested in that. Thanks.
This code will work on selected cells .This will highlight if any spell mistakes in a cell
Dim Myrange As Range
Selection.SpecialCells(xlVisible).Select
For Each Myrange In Selection
If Application.CheckSpelling(word:=Myrange.Value) = False Then
Myrange.Font.Color = vbRed
End If
Next
OK, so you can use the following command to invoke the toolbar's spellchecker which does move you to the position of the spelling error as long as you have screen updating enabled at the time.
Application.CommandBars("Tools").Controls("Spelling...").Execute
You can use this command embedded in the loop above to loop through the sheets in the workbook and invoke this command on each new sheet.
Cap
This uses a code snippet from a previous answer to restrict the area used for spellchecking to a specific region. Something I needed to do in a small project. This give the full functionallity of the spellchecker with errors shown in context. The rowNumber is calculated elsewhere.The selection range can be fully controlled elsewhere in your code to suit your particular need. Thought this might help others searching this posting.
With Sheets("Sheet1")
slic = CStr(rowNumber)
.Range("AL3:AN" & slic).Select
Application.CommandBars("Tools").Controls("Spelling...").Execute
End With
Thanks to previous posters this solved a problem form me. I am most grateful.