Excel VBA Copy/Paste Without clipboard Define Range - excel

Sorry if this had been discussed already, I am struggling with the syntax of the ranges that I need to copy and paste. I am trying to do so without using the clipboard and found out that I could do it with .Value = .Value (Excel VBA Copy and Paste (without using Copy+Paste functions) on empty row)
There are two workbooks, I am copying from wbsource.Worksheets(1) to wb1.ws
The argument is - if column L&row has a "Yes" then ... E.g. if L5 equals Yes, then copy A5:L5 and paste in the first empty row in wb1.ws.
Part of my code is
If .Cells(rw, 12) = "Yes" Then
Dim lastrw As Long
lastrw = ws.Cells(ws.Rows.Count, 1).End(xlUp).Offset(1, 0).Row
wbsource.Worksheets(1).???? = ws.Range("A" & lastrw).Value
End If
I would really appreciate it if you could help me with the syntax of this

I believe the following should work for you:
If wbsource.Worksheets(1).Cells(rw, 12) = "Yes" Then
Dim lastrw As Long
lastrw = ws.Cells(ws.Rows.Count, 1).End(xlUp).Offset(1, 0).Row
ws.Range("A" & lastrw & ":L" & lastrw).Value = wbsource.Worksheets(1).Range("A" & rw & ":L" & rw).Value
End If

Related

How to change destination ranges when using autofill in VBA

I am quite new to VBA, I am making a macro where it copies data from 'Sample' workbook to 'Etracker' workbook. After copying cell "H11" from 'Sample' to the last row of C column in Etracker, I want C column to autofill down to until where data is in J column. the code works but where it says "C16926" now, it changes every time because I constantly entering data into it. so I want to autofill it from last row in column C all the time.
I tried different ways to change "C16926" to vary but it doesn't seem to work.
Please help! and thanks in advance.
LastrowC = Etracker.Cells(Rows.Count, 3).End(xlUp).Offset(1, 0).Row
LastrowJ = Etracker.Cells(Rows.Count, 10).End(xlUp).Offset(1, 0).Row
Sample.Activate
Range("H11").Copy
Etracker.Cells(LastrowC, 3).PasteSpecial xlPasteValues
Etracker.Activate
Etracker.Range("C" & LastrowC).Select
Selection.AutoFill Destination:=Range("C16926:C" & Range("J" & Rows.Count).End(xlUp).Row), Type:=xlFillCopy
You just need to concatenate your last row into the address like
"C" & LastrowC & ":C" & Range("J" & Rows.Count).End(xlUp).Row)
Note that you don't need to .Select or .Activate if you specify a worksheet for every Range, Cells, Rows and Columns object. Using .Select or .Activate is very unreliable and makes your code really slow.
So you should end up with something like
Dim LastRowC As Long
LastrowC = Etracker.Cells(Etracker.Rows.Count, "C").End(xlUp).Offset(1, 0).Row
Dim LastrowJ As Long
LastrowJ = Etracker.Cells(Etracker.Rows.Count, "J").End(xlUp).Offset(1, 0).Row
Sample.Range("H11").Copy
Etracker.Cells(LastrowC, "C").PasteSpecial xlPasteValues
Etracker.Range("C" & LastrowC).AutoFill Destination:=Etracker.Range("C" & LastrowC & ":C" & LastrowJ), Type:=xlFillCopy

Copy/Paste last two rows into next empty row and clear certain cells (contains merged cells)

I'm trying to make a command button up the top of my sheet which when pressed will copy the last 2 rows in columns A:AJ that have data and paste into the next empty row below them. I want the source style and formulas to be copied but not the manually entered data. I have an image here too to help:
So for example from the image. I want to copy rows 105/106 together and then paste them to 107/108 as they are the next empty rows(although hidden so would also need to unhide those rows).
Everything in those 2 rows should be copied except the bottom "strokes" section and par/strokes box is a formula/date/data validation/dropdown which I want copied but the strokes section to be empty as well as date/dropdown be blank too. I would like it to all look the same as well (copy the style). Filled cells to clear in that scenario would be column B, C, E:M, P:X but only on the "STROKES" row.
To put it even more basically. I want a button to push that will add another row to the table. So I have 52 there in the picture you can see, when pressed I will now have 53 below it and it be blank ready for use.
If the hidden rows need to be unhidden for this to work I can do that.
I have looked to try do it myself but I've never done anything with VBA before so I have no idea.
I hope someone can understand this request and that it is even doable.
Thanks.
Based on DecimalTurn's answer, I made some changes and here's my new code:
Private Sub CommandButton1_Click()
'Find the last row based on column D (4th)
Dim LastRow As Long
LastRow = ActiveSheet.Cells(ActiveSheet.Rows.Count, 4).End(xlUp).Row
'Copy the range
ActiveSheet.Range("A" & (LastRow - 1) & ":" & "AJ" & LastRow).Copy
ActiveSheet.Range("A" & (LastRow + 1) & ":" & "AJ" & LastRow + 2).PasteSpecial
Application.CutCopyMode = False 'This will clear the clipboard
'Adjust numbering
ActiveSheet.Range("A" & LastRow + 1).Value2 = ActiveSheet.Range("A" & LastRow - 1).Value2 + 1
'Clear content
Dim ListOfColumnsToClear1() As Variant
Dim ListOfColumnsToClear2() As Variant
ListOfColumnsToClear1 = Array("B:C")
ListOfColumnsToClear2 = Array("E:M", "P:X")
Dim i As Long
For i = LBound(ListOfColumnsToClear1) To UBound(ListOfColumnsToClear1)
Intersect(ActiveSheet.Range("A" & (LastRow + 1) & ":" & "AJ" & LastRow + 2), ActiveSheet.Range(ListOfColumnsToClear1(i))).ClearContents
Next i
For i = LBound(ListOfColumnsToClear2) To UBound(ListOfColumnsToClear2)
Intersect(ActiveSheet.Range("A" & (LastRow + 2) & ":" & "AJ" & LastRow + 2), ActiveSheet.Range(ListOfColumnsToClear2(i))).ClearContents
Next i
End Sub
It's probably completely wrong but it did work.
To achieve what you are trying to do with VBA, I would suggest to have your code do the following (in that order):
Find the last row of data.
Define the range to copy and copy that range.
Ajust line numbering
Clear the content of the cells that need manual inputs.
Assuming you don't need to unhide any rows, the code would look like this:
Sub CopyLastTwoRows()
'Find the last row based on column D (4th)
Dim LastRow As Long
LastRow = ActiveSheet.Cells(ActiveSheet.Rows.Count, 4).End(xlUp).Row
'Copy the range
ActiveSheet.Range("A" & (LastRow - 1) & ":" & "AJ" & LastRow).Copy
ActiveSheet.Range("A" & (LastRow + 1) & ":" & "AJ" & LastRow + 2).PasteSpecial
Application.CutCopyMode = False 'This will clear the clipboard
'Adjust numbering
ActiveSheet.Range("A" & LastRow + 1).Value2 = ActiveSheet.Range("A" & LastRow - 1).Value2 + 1
'Clear content
Dim ListOfColumnsToClear() As Variant
ListOfColumnsToClear = Array("B:C", "E:M", "P:X")
Dim i As Long
For i = LBound(ListOfColumnsToClear) To UBound(ListOfColumnsToClear)
Intersect(ActiveSheet.Range("A" & (LastRow + 2) & ":" & "AJ" & LastRow + 2), ActiveSheet.Range(ListOfColumnsToClear(i))).ClearContents
Next i
End Sub
Now, since you have merged cells, the section where we clear data will give you an error since only the bottom part of your merged cells will intersect. To solve this, we can use a function that will make sure that if there are merged cells in our range, all their cells will be included.
The code would look like this (note the new function at the end):
Sub CopyLastTwoRows()
'Find the last row based on column D (4th)
Dim LastRow As Long
LastRow = ActiveSheet.Cells(ActiveSheet.Rows.Count, 4).End(xlUp).Row
'Copy the range
ActiveSheet.Range("A" & (LastRow - 1) & ":" & "AJ" & LastRow).Copy
ActiveSheet.Range("A" & (LastRow + 1) & ":" & "AJ" & LastRow + 2).PasteSpecial
Application.CutCopyMode = False 'This will clear the clipboard
'Adjust numbering
ActiveSheet.Range("A" & LastRow + 1).Value2 = ActiveSheet.Range("A" & LastRow - 1).Value2 + 1
'Clear content
Dim ListOfColumnsToClear() As Variant
ListOfColumnsToClear = Array("B:C", "E:M", "P:X")
Dim i As Long
For i = LBound(ListOfColumnsToClear) To UBound(ListOfColumnsToClear)
ExpandToIncludeMergedCells(Intersect(ActiveSheet.Range("A" & (LastRow + 2) & ":" & "AJ" & LastRow + 2), ActiveSheet.Range(ListOfColumnsToClear(i)))).ClearContents
Next i
End Sub
Private Function ExpandToIncludeMergedCells(ByRef Rng As Range) As Range
Dim TempRange As Range
Set TempRange = Rng.Cells(1)
Dim c As Range
For Each c In Rng
Set TempRange = Union(TempRange, c.MergeArea)
Next c
Set ExpandToIncludeMergedCells = TempRange
End Function
Finally, if you want to do this multiple times (say 10 times) by pressing a button, you would simply do:
Private Sub CommandButton1_Click()
Application.ScreenUpdating = False
Dim i As Long
For i = 1 To 10
CopyLastTwoRows
Next i
Application.ScreenUpdating = True
End Sub
Note that I'm using Application.ScreenUpdating = False to tell Excel not to refresh the screen while the macro is running. This will make your code run much faster, but it's recommended to set it back to true at the end and to have some error handling (which I didn't include here).

Auto-filing a formula to the last row of a column

I need to constantly start at cell R2 and auto-fill a formula down to the last row of column R. However, the number of rows is constantly changing so I need to write a macro that finds the last row and stops there. My code, as it stands right now, will auto-fill column R to the end of the worksheet (not to the row where my data stops). How do I get the auto-fill to stop at the correct row where there is no longer any data?
Sub InvoicePrice()
Dim Lastrow As Long
Lastrow = Range("P" & Rows.Count).End(xlDown).Row
Range("R2").Select
ActiveCell.FormulaR1C1 = "=RC[-2]/RC[-4]"
Selection.AutoFill Destination:=Range("R2:R" & Lastrow)
End Sub
Try this (avoids select/activate):
Dim Lastrow As Long
'Lastrow = Range("P" & Rows.Count).End(xlDown).Row
Lastrow=Cells(Rows.Count, "P").End(xlUp).Row
Range("R2:R" & Lastrow).FormulaR1C1 = "=RC[-2]/RC[-4]"
set r = range("R2")
If r.Offset(1, 0) <> "" Then
lastRow = r.End(xlDown).row
Else
lasRow = r.row
End If

Excel 2010 VBA Help Copying Ranges of Columns

My code works almost as desired. It checks all sheets for a certain value in Column "F" and then copies its associated row to the active sheet. I can make the code copy the entire row, singular columns "A", or sequential ranges "A:C". I cannot seem to make it copy specific columns like "A" "C" & "F" which is what I need it to do.
Public Sub List()
Dim ws As Worksheet
Dim i As Integer
ActiveSheet.Rows("3:" & ActiveSheet.Rows.Count).Clear
Selection.Clear
For Each ws In Worksheets
If ws.Name <> ActiveSheet.Name Then
For i = 1 To ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
If ws.Cells(i, "F").Value = "Pending" Then
ws.Cells(i, "A").Columns("A:D").Copy Destination:=ActiveSheet.Range("A" & Rows.Count).End(xlUp).Offset(1, 0)
End If
Next
End If
Next
As following from comments to Question, correct answer is to use following line:
ws.Range("A" & i & ", C" & i & ", E" & i).Copy Destination:=ActiveSheet.Range("A" & Rows.Count).End(xlUp).Offset(1, 0)
instead
ws.Cells(i, "A").Columns("A:D").Copy Destination:=ActiveSheet.Range("A" & Rows.Count).End(xlUp).Offset(1, 0)

VBA, how to insert a dynamic / relative cell reference into a .formulaArray method?

I have the following code:
With Sh.Range("A1:A" & LastRow + 1).Offset(0, 4)
.FormulaArray = "=MAX(IF(A2:A" & LastRow + 1 & "= ** , D2:D" & LastRow + 1 & "))"
.Value = .Value
End With
In the place where I have **, I would want a dynamic cell reference. If I was using .formulaR1C1, I would have inserted RC[-1], but I can't use that with a .formulaArray.
Does anyone know how I can insert a relative cell reference that would change as the formula being pasted within the range?
Thank you
EDIT # 1
The whole code looks like this:
Sub RemoveDuplicates_SumMarketValue()
Dim Sh As Worksheet
Dim LastRow As Long
Dim Rng As Range
Set Sh = Worksheets(1)
Sh.Columns(6).Insert
LastRow = Sh.Range("A65536").End(xlUp).Row
With Sh.Range("A1:A" & LastRow).Offset(0, 5)
.FormulaR1C1 = "=IF(COUNTIF(R1C[-5]:RC[-5],RC[-5])>1,"""",SUMIF(R1C[-5]:R[" & LastRow & "]C[-5],RC[-5],R1C[-1]:R[" & LastRow & "]C[-1]))"
.Value = .Value
End With
Sh.Columns(5).Delete
Sh.Rows(1).Insert
Sh.Columns(5).Insert
With Sh.Range("A1:A" & LastRow + 1).Offset(0, 4)
.FormulaArray = "=MAX(IF(A2:A" & LastRow + 1 & "= A1 , D2:D" & LastRow + 1 & "))"
.Value = .Value
End With
Set Rng = Sh.Range("E1:E" & LastRow + 1)
With Rng
.AutoFilter Field:=1, Criteria1:="="
.SpecialCells(xlCellTypeVisible).EntireRow.Delete
End With
End Sub
This purpose of this code, is too look though a sample of data and
find duplicates
sum up values in 5th column associated with duplicates
remove duplicate rows (except the one that carries the sum from 5th column)
Now I also want it to have the max value from column 4th of all the duplicates to be retained in the final version, but I can't get the array formula to reference the row correctly.
EDIT : Try pasting this inside the "ThisWorkbook" code sheet :
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
Dim Sh As Worksheet
Dim LastRow As Long
Dim Rng As Range
Set Sh = Worksheets(1)
LastRow = Sh.Range("A65536").End(xlUp).Row
With Sh.Range("A1:A" & LastRow).Offset(0, 5)
.FormulaR1C1 = "=IF(COUNTIF(R1C[-5]:RC[-5],RC[-5])>1,"""",SUMIF(R1C[-5]:R[" & LastRow & "]C[-5],RC[-5],R1C[-1]:R[" & LastRow & "]C[-1]))"
.Value = .Value
End With
With Sh.Range("A1:A" & LastRow + 1).Offset(0, 4)
.FormulaArray = "=MAX(IF(A2:A" & LastRow + 1 & "= A1 , D2:D" & LastRow + 1 & "))"
.Value = .Value
End With
'This section you might want to remove from this routine
Set Rng = Sh.Range("E1:E" & LastRow + 1)
With Rng
.AutoFilter Field:=1, Criteria1:="="
.SpecialCells(xlCellTypeVisible).EntireRow.Delete
End With
End Sub
It basically is the same as your function, but it doesn't remove the columns or add any rows. What will happen is everytime one of your cell's content changes, this macro will run automatically, updating the formulas in the cells.
The closest you can get to achieving that, is having a macro in the background that will be running everytime a change is made to the sheet. If you have tens of thousands of rows, or a REALLY slow computer, this may not be the ideal solution. If this is not the case, however, you may find it very easy to get your code to work with very little changes.
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
'Update your formula here with the new content/reference
' (your code + some changes to update where the last row is)
End Sub
Paste this inside the "ThisWorkbook", and simply place your code inside it.
This is what I came up with to solve the issue of the .formulaArray not accepting RC cell reference notation. I just used a loop to insert the array formula into each cell and reference the target row by using the loop variable i.
Code:
Sub RemoveDuplicates_SumMarketValue()
Dim Sh As Worksheet
Dim LastRow As Long
Dim Rng As Range
Dim targetcell As Range
Set Sh = Worksheets(1)
Sh.Columns(6).Insert
LastRow = Sh.Range("A65536").End(xlUp).Row
With Sh.Range("A1:A" & LastRow).Offset(0, 5)
.FormulaR1C1 = "=IF(COUNTIF(R1C[-5]:RC[-5],RC[-5])>1,"""",SUMIF(R1C[-5]:R[" & LastRow & "]C[-5],RC[-5],R1C[-1]:R[" & LastRow & "]C[-1]))"
.Value = .Value
End With
Sh.Columns(5).Delete
Sh.Rows(1).Insert
Sh.Columns(5).Insert
For i = 2 To LastRow + 1
Cells(i, 5).FormulaArray = "=MAX(IF(A2:A" & LastRow + 1 & "= A" & i & " , D2:D" & LastRow + 1 & "))"
Cells(i, 5) = Cells(i, 5).Value
Next
Sh.Columns(4).Delete
Set Rng = Sh.Range("E1:E" & LastRow + 1)
With Rng
.AutoFilter Field:=1, Criteria1:="="
.SpecialCells(xlCellTypeVisible).EntireRow.Delete
End With
Sheets(1).Cells(1, 4) = "Price"
Sheets(1).Cells(1, 5) = "market value"
End Sub
So what this code does, loops for duplicates in Col 1, sums up associated values in col 5 and picks the max associated value in col 4.
Could you use, where you fill a cell and replicate it,
L = LastRow + 1
With Sh.Range("A1:A" & L).Offset(0, 4)
.Cells(1,1).FormulaArray = "=MAX(IF(A$2:A$" & L & "=A1,D$2:D$" & L & "))"
.FillDown
.Value = .Value
End With
Handling A1 vs R1C1 style is easy, with Application.ConvertFormula
Need to be careful about Row/Col Abs/Rel referencing.

Resources