I am writing an Excel macro in VBA. In a certain part of my code, I need to copy and paste one unmerged cell to a merged cell in a separate workbook. Part of this process is repetitive. The most efficient way for me to achieve this is to copy the cell and then Call a separate Sub with the location it will be pasted into. I have written a version using Select that works:
Sub Macro1()
Workbooks("Book1.xlsm").Worksheets("Sheet1").Activate
ActiveSheet.Range("A1").Copy
Call Macro2
End Sub
Sub Macro2()
Workbooks("Protected_JD_Form.xlsx").Worksheets("Cover Page").Activate
ActiveSheet.Range("E46:G46").Select
Selection.PasteSpecial Paste:=xlPasteFormulasAndNumberFormats, Operation:= _
xlNone, SkipBlanks:=False, Transpose:=False
End Sub
But I have been trying to move away from Select and this is the code I came up with:
Sub Macro1()
Workbooks("Book1.xlsm").Worksheets("Sheet1").Range("A1").Copy
Call Macro2
End Sub
Sub Macro2()
Workbooks("Protected_JD_Form.xlsx").Worksheets("Cover Page").Range("E46:G46").PasteSpecial xlPasteFormulasAndNumberFormats
End Sub
This code gives me a 'PasteSpecial method of Range class failed' on the only line of Macro 2.
Is there a way to copy the contents of a cell and then Call a separate Sub with the destination and paste method, without using Select?
Edit: I understand that setting the values equal to each other is simpler than copy/paste. However, in this instance, that is not a possibility.
I also understand the pitfalls of merged cells. I get it. Seriously. But I don't have a choice as I am working with a customer's workbook, which can't be changed.
Edit #2: Following a comment from #ExcelHero, I rebooted my computer and ran the macro again. It worked. I have no idea what was going wrong. Regardless, credit to #ExcelHero for recognizing that my code was correct.
Why a second macro?
Anyways, if it's absolutely necessary, you're going to need to edit your second macros slightly.
Sub Macro1()
Dim copyString As String
copyString = Worksheets("Sheet1").Range("A1").Value
Call Macro2(copyString)
End Sub
You'll notice that now Macro2 is called with copyString after it. This means, that you will run Macro2, and do something with whatever value is set to copyString.
Sub Macro2(text As String)
'Workbooks("Protected_JD_Form.xlsx").Worksheets("Cover Page").Range("E46:G46").Value = text
Workbooks("Protected_JD_Form.xlsx").Worksheets("Cover Page").Range("E46:G46").Value = text
End Sub
In this macro, you'll see I added (text as String) to the macro name. This will take some value (from Macro1, the value is the copyString variable), and then use it in the macro. Does that make sense?
Again, I would only do this way if you absolutely need two macros for this copy/pasting. If you can use just one macro, then these can be combined into a few simple lines:
Sub Macro3()
Dim copyString As String
copyString = Worksheets("Sheet1").Range("A1").Value
Workbooks("Protected_JD_Form.xlsx").Worksheets("Cover Page").Range("E46:G46").Value = copyString
End Sub
...or even further, cut out the copyString middleman (as #findwindow mentioned):
Sub macro4()
Workbooks("Protected_JD_Form.xlsx").Worksheets("Cover Page").Range("E46:G46").Value = Worksheets("Sheet1").Range("A1").Value
End Sub
This format worked for me - simply defining your copy range as a range and passing it to the first sub, and then iterating through the loop of declaring subsequent cells as the paste range, and passing that range on to the pasting sub:
Sub copy_paste()
Dim rng As Range
Set rng = Sheets(1).Range("A1")
Call copy_start(rng)
End Sub
Sub copy_start(ByVal copyRange As Range)
Dim rng As Range
copyRange.copy
For i = 1 To 5
Set rng = Sheets(1).Range("B" & i)
Call pasteover(rng)
Next i
Application.CutCopyMode = False
End Sub
Sub pasteover(ByVal pasteRange As Range)
pasteRange.PasteSpecial Paste:=xlPasteFormulasAndNumberFormats, Operation:= _
xlNone, SkipBlanks:=False, Transpose:=False
End Sub
The error resulted from the copy/pasting that I was doing while I was editing my code. Because the code I was copying was still on the clipboard, the macro tried to paste that into the cell, causing the error.
Lessons Learned:
Limit use of the clipboard in VBA
When copy/pasting in the coding window, double check to make sure that you are not affecting the existing code
Related
I once built a VBA button to automatically lock all cells with data in them. And it was working perfectly. Now I wanted to copy that button to another worksheet. So I created another button, copy and pasted the whole VBA over, then edited the worksheet names and range. And, it's only working like 5% of the time, the rest of the time, I'm getting an "Run-Time error '1004': No cells were found." I've tried a few fixed, changing Sheets to Worksheets, or adding a ", 23" to the specialcells argument. However, nothing is working right now. When I try stepping in, it sometimes say both rng and lckrng as empty, and sometimes only show lockrng as empty and not show rng at all. Problem is this used to be a working code, and now, it still works around 5% of time. Any idea why? Thank you very much!
Private Sub CommandButton1_Click()
Dim rng As Range
Dim lockrng As Range
Sheets("Uploading Checklist (M)").Unprotect Password:="signature"
Set rng = Range("A1:M14")
'Selecting hardcoded data and formulas
Set lockrng = Union(rng.SpecialCells(xlCellTypeConstants), rng.SpecialCells(xlCellTypeFormulas))
lockrng.Locked = True
Sheets("Uploading Checklist (M)").Protect Password:="signature"
End Sub
Maybe this is too simplistic, but it seems to do what you want. The animated .gif shows it working to "lock all cells with data in them". (I made the second button just for convenience). If nothing else it might be good to start from something like this that works and modify to suit your needs.
Dim cell As Range, sh As Worksheet
Sub Button4_Click()
Set sh = Worksheets("Sheet1")
sh.Unprotect Password:="s"
For Each cell In sh.UsedRange
If cell <> "" Then cell.Locked = True Else cell.Locked = False
Next
sh.Protect Password:="s"
End Sub
Sub Button5_Click()
Set sh = Worksheets("Sheet1")
sh.Unprotect Password:="s"
End Sub
The Union you are attempting will not work if either of the parameters is Nothing (i.e. you either have no constants in the range, or you have no formulas in the range).
Prior to doing the Union, you should check the parameters aren't Nothing but, once you start changing your code to do that, it would be just as simple to do the locking in two parts - so I recommend you rewrite the code as follows:
Private Sub CommandButton1_Click()
With Sheets("Uploading Checklist (M)")
.Unprotect Password:="signature"
With .Range("A1:M14")
'Lock any constants
If Not .SpecialCells(xlCellTypeConstants) Is Nothing Then
.SpecialCells(xlCellTypeConstants).Locked = True
End If
'Lock any formulas
If Not .SpecialCells(xlCellTypeFormulas) Is Nothing Then
.SpecialCells(xlCellTypeFormulas).Locked = True
End If
End With
.Protect Password:="signature"
End With
End Sub
I have a problem with my macro to copy paste only the values of the range A6:AM46,A52:AM84 to AN6 location on the same sheet.
Sub PréparerGrilles()
Range("A6:AM46,A52:AM84").Select
Selection.Copy
Range("AN6").Select
Application.CutCopyMode = False
ActiveSheet.Paste
End Sub
I get the 1004 error (multiple selection error)
Could you help me with this ?
To copy values from A6:AM46,A52:AM84 to AN6:BZ46,AN52:BZ84 you can do the following:
Sub PreparerGrilles()
Range("AN6:BZ46").Value = Range("A6:AM46").Value
Range("AN52:BZ84").Value = Range("A52:AM84").Value
End Sub
Version using the Range.Copy method:
Sub PreparerGrilles()
Range("A6:AM46").Copy Destination:=Range("AN6:BZ46")
Range("A52:AM84").Copy Destination:=Range("AN52:BZ84")
Range("AN6:BZ46").Value = Range("AN6:BZ46").Value
Range("AN52:BZ84").Value = Range("AN52:BZ84").Value
End Sub
I recommend that you don't slow your code down by using this. It will also lead to potentially incorrect values if your formulae refer to anything that wasn't part of the copy.
Version using a PasteSpecial xlPasteValues method:
Sub PreparerGrilles()
Range("A6:AM46").Copy
Range("AN6:BZ46").PasteSpecial xlPasteValues
Range("A52:AM84").Copy
Range("AN52:BZ84").PasteSpecial xlPasteValues
End Sub
I strongly recommend against using this method, as it leads to too many "unreproducible" errors due to the users copying things via the clipboard between when your code does the Copy and when it does the Paste, and also because of the fact that your Copy zaps whatever the user might have manually pasted to the clipboard.
Application.CutCopyMode = False cleans the clipboard....
Better code for that is:
Sub Test()
Dim wb As Workbook, ws As Worksheet
Set wb = ThisWorkbook
Set ws = wb.Sheets("SOMMAIRE") 'this means it will only work on this sheet, you should change the name of the sheet or ask me what do you want in order to get it working on other sheets
ws.Range("A6:AL46").Copy
ws.Range("AN6").PasteSpecial xlValues
ws.Range("A52:AM84").Copy
ws.Range("AN52").PasteSpecial xlValues
End Sub
Edited: Now that should do the trick. Try it out and tell me if it works
Edited2: This is what you want, at least for what you asked so far.
SOLVED: FOUND MY OWN WORKSHEET ERROR
The problem I was having was trying to use two worksheet_change events in the same workbook. Because I thought that was possible, I was just renaming the worksheet event in question when I received an error, thinking nothing of it. Both my original code and the answer provided work, when combined with my other worksheet_change event.
Thanks everyone.
Original Request:
I am trying to run a macro that does this:
every time cell r6 changes, run a macro that looks to see if the value in cell s9 is > or < 1, then format cells s9:t100 based on that.
I have the macro on its own to do the second part:
sub macro1()
If Range("S9").Value < 1 Then
Range("S9:S100,T9:T100").Select
Selection.NumberFormat = "0.0%"
Else
Range("S9:S100,T9:T100").Select
Selection.NumberFormat = "#,##0"
End If
end sub
This macro run on its own, works exactly as I want and formats the cells.
Then I have the worksheet event to call up that macro:
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Address = "$R$6" Then
Call Macro1
End If
End Sub
When it is run to call up the same macro, it does not format the cells. They just stay as a % regardless of when cell r6 changes.
Any ideas why the worksheet event causes the macro to not work?
Try passing the worksheet object to your macro. This fully qualifies the Ranges to make sure you're working on the right area.
Also, you don't need to Select at all. Just use the range and directly change the settings.
Public Sub Macro1(ws as Worksheet)
If ws.Range("S9").Value < 1 Then
ws.Range("S9:S100,T9:T100").NumberFormat = "0.0%"
Else
ws.Range("S9:S100,T9:T100").NumberFormat = "#,##0"
End If
end sub
Sub test()
Macro1 ActiveSheet
End Sub
And in your Worksheet_Change...
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Address = "$R$6" Then
Macro1 Target.Worksheet
End If
End Sub
I am trying to copy over the cell values and contents over to a new, locked worksheet, but currently the code is bringing over the original worksheet's formatting. Currently the code I am using is:
Sub sbCopyRangeToAnotherSheet()
Sheets("LTL Quote Form").Range("A1:L33").Copy Destination:=Sheets("Sheet3").Range("A1")
Application.CutCopyMode = False
End Sub
And I've even tried:
Sub Copy()
Sheets("LTL Quote Form").Range("A5:L11").Copy
Sheets("Sheet1").Range("A5:L11").PasteSpecial xlPasteValues
End Sub
But neither of them are working. Any suggestions on how to copy over the cell values, but not the formatting?
The second example you posted will paste values, not formatting. You could also use:
Sub sbCopyRangeToAnotherSheet()
Sheets("Sheet3").Range("A1:L33").Value2 = Sheets("LTL Quote Form").Range("A1:L33").Value2
End Sub
I have 20 excel workbooks - each identical except that they work with different data. I often need to refine and update the formulae, which I then copy from a source workbook to each of the other workbooks in turn.
I use VBA to record a macro (which I know is not that elegant) to copy and paste the changes required. This is fine, but it's tedious to have to copy the macro 20 times, changing the target workbook name each time. My preferred solution is to automate it by setting up a loop and calling the macro for each target in turn.
I am new to macros, and am struggling with the right syntax for this.
I've tried the following, but it does not work. I get a "Object Variable not set" error message, which i don't really understand and can't resolve.
Sub New()
'
Dim i As Integer
Dim target As Workbook
target(1) = "Workbook1.xlsx"
target(2) = "Workbook2.xlsx"
'etc for the other countries
For i = 1 To 20
Update
Next i
End Sub
Sub Update()
' Update macro copies updated cells from workbook Country A.xlsx to target workbook
Windows("Country A.xlsx").Activate
Sheets("Tax").Select
Rows("17:26").Select
Selection.Copy
Windows(target(i)).Activate
Sheets("Tax").Select
Range("A17").Select
ActiveSheet.Paste
' Etc for other changes required
End Sub
Any help on what i'm missing would be much appreciated.
Sub Update is not "seeing" the variable i. Consider:
Sub New()
Dim i As Integer
Dim target As Workbook
target(1) = "Workbook1.xlsx"
target(2) = "Workbook2.xlsx"
'etc for the other countries
For i = 1 To 20
Update (i)
Next i
End Sub
Sub Update(i As Integer)
' Update macro copies updated cells from workbook Country A.xlsx to target workbook
Windows("Country A.xlsx").Activate
Sheets("Tax").Select
Rows("17:26").Select
Selection.Copy
Windows(target(i)).Activate
Sheets("Tax").Select
Range("A17").Select
ActiveSheet.Paste
' Etc for other changes required
End Sub
After trying a few different things, i've found a solution to avoid the problems with the above code. It involves setting up the names of the various target worksheets in an array.
The code below works
Sub Newtest()
'
Dim target As String
Dim targetwb(1 To 2) As String
targetwb(1) = "Workbook3.xlsx"
targetwb(2) = "Workbook4.xlsx"
'
For i = 1 To 2
target = targetwb(i)
Test1 (target)
Next i
End Sub
Sub Test1(target)
'
' Copies updated cells from workbook Country A.xlsx to target workbook
'
Windows("Country A.xlsx").Activate
Sheets("Tax").Select
Rows("17:26").Select
Selection.Copy
Windows(target).Activate
Sheets("Tax").Select
Range("A17").Select
ActiveSheet.Paste
'
End Sub