Paste Special Not Working more than 1 time: VBA - vba

I have 2 sheets in my workbook, "Sheet1" and "Data". In Sheet1 I have used a Worksheet_Change macro so that when a change happens in column C:
A timestamp appears in column D
That range will get copied into the "Data" sheet.
Here is my code:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim Location As Range
If Target.Column > 3 Or Target.Column < 3 Then Exit Sub
Application.EnableEvents = False
Cells(Target.Row, 4) = Now
Application.EnableEvents = True
Selection.End(xlToLeft).Select
Range(Selection, Selection.End(xlToRight)).Select
Selection.Copy
Sheets("data").Unprotect
Sheets("data").Range("a1").End(xlDown).Offset(1, 0).PasteSpecial _
Paste:=xlPasteValues, Operation:=xlNone, _
SkipBlanks:=False, Transpose:=False
Sheets("data").Protect
Range("a1").Select
End Sub
My problem is that the PasteSpecial is not working more than one time.

The problem was that unprotecting the sheet was clearing the clipboard, meaning there was nothing to paste! Here is adapted code, which I've also changed in a couple of other ways to greatly improve it, see the comments for details.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim Location As Range
' Use <> to mean "not equal to"
If Target.Column <> 3 Then Exit Sub
Application.EnableEvents = False
' Fully qualify the cells object
ThisWorkbook.Sheets("Sheet1").Cells(Target.Row, 4).Value = Now
Application.EnableEvents = True
' Avoid using .Select and Selection, the user could have clicked anywhere after the value change
' Use a With block to fully qualify your range objects
With ThisWorkbook.Sheets("data")
.Unprotect
' Copy immediately before paste
Target.EntireRow.Copy
.Range("A1").End(xlDown).Offset(1, 0).PasteSpecial Paste:=xlPasteValues
.Protect
End With
Application.CutCopyMode = False
End Sub
Currently, this just overwrites the same line on the "data" sheet, because the data you're pasting has nothing in column A, so the End(xlDown) in column A returns the same position. You may need to change this to column C, or use
.Cells(Rows.Count,"C").End(xlUp).Offset(1, 0).PasteSpecial
Which is still column dependant but goes up to get the last row. There is a dot . before Cells because that line would be within the With block.

Not sure what is being copied as you have used Selection and that depends upon how you input value in column C, whether by hitting Enter or Ctrl+Enter.
Say if you input a value in B2 and press Enter to submit it, cell B3 will get selected and as per your code a range from row3 will be copied to Data sheet. Whereas if you hit Ctrl+Enter, the selection will remain in B2 so a range from row2 will be copied to data sheet.
But that you can tweak yourself.
See if the tweaked code works for you.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim Location As Range
If Target.Column <> 3 Then Exit Sub
Application.ScreenUpdating = False
Application.EnableEvents = False
Cells(Target.Row, 4) = Now
Application.EnableEvents = True
Selection.End(xlToLeft).Select
Range(Selection, Selection.End(xlToRight)).Select
Sheets("data").Unprotect
Selection.Copy
Sheets("Data").Range("A" & Rows.Count).End(3)(2).PasteSpecial xlPasteValues
Sheets("data").Protect
Range("a1").Select
Application.ScreenUpdating = True
End Sub

Related

edit data on the sheet from master sheet

I have a file that consists of 5 data related sheets and an additional master sheet. These master sheet has a functionality that retrieves the data from all sheets into master sheet based on the ID.
The flow of the code for now is:
Range().Select
Application.CutCopyMode = False
Selection.Copy
Sheets("mSheet").Select
ActiveSheet.Paste
It does what I want in a simple way that it only shows the necessary data. What I ideally want is to have link between this data and actual sheet, so that once I retrieve data in the master sheet and perform any edit - this edit is made in the actual corresponding sheet.
Any idea and suggestion is appreciated.
Original partial VBA code:
Sheets("dSheet1").Select
ActiveSheet.ListObjects("Table").Range.AutoFilter Field:=3, Criteria1:=id
Range(Selection, Selection.End(xlDown)).Select
Range(Selection, Selection.End(xlToRight)).Select
Application.CutCopyMode = False
Selection.Copy
Sheets("masterSheet").Select
ActiveSheet.Paste Destination:=Sheets("masterSheet").Range("A8")
The following will copy the contents of your selection, but instead of pasting the value will enter the reference to the cell, so when the cell gets updated, so would the master sheet:
Sub foo()
Range("A1").Select
Sheets("mSheet").Range("A2").Formula = "=" & Selection.Address
End Sub
EDIT
The following will do the opposite of the code above, so it will copy the data, paste it in your master Sheet and then go back to the original copied range and enter the cell reference there so when the master is updated, so is that range:
Sub foo()
Range("A1").Select 'select the range to be copied
Application.CutCopyMode = False
Selection.Copy 'copy it
Sheets("mSheet").Range("A2").PasteSpecial (xlPasteValues) 'paste the value into your master sheet
Range("A1").Formula = "=" & Sheets("mSheet").Range("A2").Address
'go back to your previous selection and enter the formula to reference the specific cell
End Sub
UPDATE
Replace your code with the following, as it does the same but with fewer lines of code an without any Select statement:
Sub foo2()
Sheets("dSheet1").ListObjects("Table").Range.AutoFilter Field:=3, Criteria1:=ID
Sheets("dSheet1").Range("A2:D2").Copy Destination:=Sheets("masterSheet").Range("A8")
'change the range above to copy as many columns as you need
End Sub
Then add the following code behind you Master Sheet to detect changes:
Private Sub Worksheet_Change(ByVal Target As Range)
'place this code behing the masterSheet
LastRow = Sheets("dSheet1").Cells(Sheets("dSheet1").Rows.Count, "A").End(xlUp).Row
'get the last row of dSheet1
If Target.Address = "$A$8" Then 'if A8 changes
For i = 1 To LastRow 'loop through dSheet1 to find the ID
If Sheets("dSheet1").Cells(i, 3) = Range("C8").Value Then 'when ID found
Sheets("dSheet1").Cells(i, 1) = Range("A8").Value 'change relevant cell with new data
End If
Next i
End If
'below do the same as above to change data for other columns, add more to adapt it to be able to make changes to however many columns you are copying over
If Target.Address = "$B$8" Then
For i = 1 To LastRow
If Sheets("dSheet1").Cells(i, 3) = Range("C8").Value Then
Sheets("dSheet1").Cells(i, 2) = Range("B8").Value
End If
Next i
End If
If Target.Address = "$D$8" Then
For i = 1 To LastRow
If Sheets("dSheet1").Cells(i, 3) = Range("C8").Value Then
Sheets("dSheet1").Cells(i, 4) = Range("D8").Value
End If
Next i
End If
End Sub
This assumes that your ID's are unique and you are getting a single row as a result of your autofilter.
How about adding buttons.
CopyID - Copy range from SheetID = B1
After Editing, EditID - will clear range in SheetID = B1, and rewrites everything from mSheet Range from A3 then paste in SheetID = B1
Edit ID code:
Public Sub EditID(ID As String)
Sheets(ID).Select
If Range("A1").Value <> "" Then
Range("A1").Select
Range(Selection, Selection.End(xlToRight)).Select
Range(Selection, Selection.End(xlDown)).Select
Selection.Clear
End If
Sheets("mSheet").Select
If Range("A3").Value <> "" Then
Range("A3").Select
Range(Selection, Selection.End(xlToRight)).Select
Range(Selection, Selection.End(xlDown)).Select
Selection.Copy
Sheets(ID).Select
Range("A1").Select
ActiveSheet.Paste
End If
Sheets("msheet").Select
End Sub

copy and paste formulas quickly

I have been trying to write a simple code that copies the value from one cell and paste its formula into all the cells in one column (There are several cells, around 3000). The code works, but it takes around 30 min to run, so it's not ok for me. I also tried to let the value of the formula without "=" and then use the replace command, but it does not work as well. Anyone could help me with that in order to run the macro in 1 min? This is the part of my code that I try to do that:
sub copy_paste
Worksheets("Formatar").Range("H1:L1").Copy
Worksheets("Formatar").Range("H3").PasteSpecial xlValue
Worksheets("Formatar").Range("H3:L3").Copy
Range(Selection, Selection.End(xlDown)).Select
Selection.PasteSpecial xlFormulas
end sub
Tell me if this help you...
Sub copy_paste()
Worksheets("Formatar").Range("H1:L1").Copy 'Copy from row 1
Worksheets("Formatar").Range("H3").PasteSpecial xlPasteValues 'paste the values to row 3
Worksheets("Formatar").Range("H3:L3").Copy 'here you copy that (the values)
Range(Selection, Selection.End(xlDown)).Select 'you select eveything from row3
Selection.PasteSpecial xlPasteValues 'and paste it... but you copy just values from 3!
End Sub
And then you paste it over the first occurrence and you lost data.
Here is my suggest.
Sub copy_paste()
Dim sht As Worksheet
Dim r
Dim H
Dim L
Set sht = Sheets("Formatar") 'store the sheet
sht.Activate 'activate it!
Range("H1:L1").Copy
Range("H3").PasteSpecial xlPasteFormulas 'Paste the formula
Range("H3:L3").Copy 'then copy again
H = Range("H1").Column 'Just to take the number of the columns H and L
L = Range("L1").Column
r = Range("H3").End(xlDown).Row - 1 'Take the number of the last blank row.
Range(Cells(3, H), Cells(r, L)).PasteSpecial xlPasteValues
'Here you paste values, of if you need the
'formula use this: xlPasteFormulas
Application.CutCopyMode = False 'never forget this...
End Sub
Edit
May be this could help...
'Application.Calculation = xlManual
Sub copy_paste()
Dim sht As Worksheet
Dim r
Dim H
Dim L
Set sht = Sheets("Formatar") 'store the sheet
sht.Activate 'activate it!
Range("H1:L1").Copy
Range("H3").PasteSpecial xlPasteFormulas 'Paste the formula
Application.Calculation = xlManual 'Not automatic calculation
Range("H3:L3").Copy 'then copy again
H = Range("H1").Column 'Just to take the number of the columns H and L
L = Range("L1").Column
r = Range("H3").End(xlDown).Row - 1 'Take the number of the last blank row.
Range(Cells(3, H), Cells(r, L)).PasteSpecial xlPasteValues
'Here you paste values, of if you need the
'formula use this: xlPasteFormulas
Application.CutCopyMode = False 'never forget this...
Calculate 'Calculate the whole sheet
Application.Calculation = xlCalculationAutomatic 'return automatic calculation
End Sub

Excel Macro: If Cell A is clicked, copy Cell B and paste as value, then return position to Cell A

I am creating a form template in Excel that has Cell B = TODAY() so, when a user opens the template to fill out the form, today's date is displayed.
However, once the user fills out the form, I would like Cell B to equal the value of TODAY(), so the cell does not update every time the Workbook is opened.
My solution to this was to create a Macro that does the following:
When the user clicks on Cell A, a cell that the user is required to fill in with text, Cell B, containing the formula =TODAY(), is copied and pasted as a value to the same position. Then, Cell A is selected again so the user can fill in the required information.
Below is my example VBA, which checks if Cell A is empty instead of being clicked on. I would like to change this though, if someone could help with that.
In this example, Cell A is E11 and Cell B is E13.
Sub Date_Change(ByVal Target As Range)
Dim Rng As Range
Set Rng = Sheets("Form").Range("E13")
If Sheets("Form").Range("E11") = Empty Then
Sheets("Form").Range("E13") = TODAY()
Else
Rng.Copy
Rng.PasteSpecial xlPasteValues
Set Rng = Nothing
Application.CutCopyMode = False
Cells(11, 5).Select
End Sub
Within the workbook object you have a built in event workbook_beforesave...
you could put the code there.
I recorded a macro which updates B3 to be a hard value, eliminating the formula.
I then copied the code out of the macro and hit save. You may need to update the "range" to account for specific worksheet if multiple sheets in workbook.
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
Range("B3").Select
Selection.Copy
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
End Sub
How's this (this goes in the worksheet you want to run it on's module):
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim cellA As Range, cellB As Range
Set cellA = Sheets("Form").Range("E11")
Set cellB = Sheets("Form").Range("E13")
If Target.Row = cellA.Row And Target.Column = cellA.Column Then
Dim Rng As Range
Set Rng = cellA
If cellA = Empty Then
cellB.Value = WorksheetFunction.Text(Month(Now()) & "/" & Day(Now()) & "/" & Year(Now()), "MM/DD/YYYY")
Else
Rng.Copy
Rng.PasteSpecial xlPasteValues
Set Rng = Nothing
Application.CutCopyMode = False
Cells(11, 5).Select
End If
End If
End Sub
You may need to check my declarations, but that should get you going. It'll check to see if cell E11 was selected, and if so, will run.

We can't paste Excel ranges because the copy area and paste area aren't the same size

I would like to loop through column A in Worksheet1 and find the first cell which has a specified text "Oil Production". This cell is the first cell in the array I wish to copy to Worksheet2. This cell and the size of the array will change from time to time, hence the code I have used. I then paste it into cell B7 in Worksheet2 which will never change.
This is my formula. I get the error at line ActiveSheet.Paste
Sub Test()
Application.ScreenUpdating = False
For Each Cell In Sheets("Sheet1").Range("A:A")
If Cell.Value = "Oil Production" Then
ActiveSheet.Cells.Select
Range(ActiveCell, Cells(ActiveCell.End(xlDown).Row, ActiveCell.End(xlToRight).Column)).Select
Selection.Copy
Sheets("Sheet2").Select
Range("B7").Select
ActiveSheet.Paste
End If
Next
End Sub
resize the area:
Sub Test()
Dim MyRowCount As Long, MyColCount As Long
Application.ScreenUpdating = False
For Each Cell In Sheets("Sheet1").Range("A1:A" & Range("A" & Rows.count).end(xlup).row) 'This make it poll the used data rather than the whole column
If Cell.Value = "Oil Production" Then
ActiveSheet.Cells.Select
With Range(ActiveCell, Cells(ActiveCell.End(xlDown).Row, ActiveCell.End(xlToRight).column))
.Copy
MyRowCount = .Rows.Count
MyColCount = .Columns.Count
End With
Sheets("Sheet2").Select
Range("B7").Resize(MyRowCount, MyColCount).PasteSpecial xlPasteAll
'Do you need to flick back to Sheet1 after pasting?
End If
Next
End Sub
Also I took out a bunch of selects for you.
Range("A1").Select
Selection.Paste
can be written as
Range("A1").PasteSpecial XLPasteAll
You can chop out most selects this way, you can see I have also done it with the Range you are copying

how to capture cell address as a variable and use in VB code?

Need a code snippet; if some kind guru could help, please. I need to express the following cursor movement sequence in XL VBA.
After entering a formula in cell A1 (Col-A is otherwise empty), I need to copy the formula to all cells in the range A1:AN, where N is the last row of the table.
I recorded a macro to do the following (code below):
1) enter the formula (in Cell A1)
2) copy the formula
3) go Right to B1
4) go to the last populated cell in Col-B [using Ctrl+Down] (easiest way to find the last row)
5) go Left to Col-A
6) select all cells from current to A1
7) paste the formula to the selection
The part I need help with is a way to capture the cell address in step 5 as a variable so that I can use this macro on a series of files having a variable number of rows.
Here is the recorded macro. In this example, the last row in the table is 7952.
Sub test()
ActiveCell.FormulaR1C1 = "=LEFT(RC[1],3)"
ActiveCell.Select
Selection.Copy
ActiveCell.Offset(0, 1).Range("A1").Select
Selection.End(xlDown).Select
ActiveCell.Offset(0, -1).Range("A1").Select
Range(Selection, Selection.End(xlUp)).Select
ActiveCell.Offset(-7951, 0).Range("A1:A7951").Select
ActiveCell.Activate
ActiveSheet.Paste
End Sub
Kindly copy the below code to the worksheet.
Private Sub Worksheet_Change(ByVal Target As Range)
Application.EnableEvents = False
Application.Calculation = xlCalculationManual
If Target.Address = "$A$1" And Target.Count = 1 And Target.HasFormula Then
Dim lastRow As Long
lastRow = Range("A65000").End(xlUp).Row
Dim rng As Range
Set rng = Range("A2:A" & lastRow)
' Target.Copy
' rng.PasteSpecial xlPasteFormulas
'OR
' rng.Formula = Target.Formula
' OR
rng.FormulaR1C1 = Target.FormulaR1C1
End If
Application.EnableEvents = True
Application.Calculation = xlCalculationAutomatic
End Sub
I'm not sure if your end cell is always going to be the same, meaning you may want to "un" hard code the rows, but you could try this.
Sub test()
Range(Cells(1, 1), Cells(7951, 1)) = "=LEFT(RC[1],3)"
End Sub
If you are always going to put equations in column A based on the number of rows used in column B you could try this.
Sub test()
' dimension the variable type
Dim lastRow As Long
' select cell "B1"
Cells(1, 2).Select
' jump to the last consecutive row in column B
Selection.End(xlDown).Select
' collect the row number into a variable
lastRow = ActiveCell.Row
' paste the equation into the variable length range
Range(Cells(1, 1), Cells(lastRow, 1)) = "=LEFT(RC[1],3)"
End Sub
Thanks Todd and user2063626,
I decided on a simpler approach. I only needed to obtain the last row in order to set my selection area; the number of the last row is not used in the actual values to be written. The files to be manipulated are flat ascii exports; the column layout is constant, only the number of rows is variable.
After writing the formula to A1, I move down column B and test for a value one cell at a time; if TRUE, copy the formula to the left adjacent cell; if FALSE, end process.
Sub FillClientCodes()
Range("A1").Select
ActiveCell.FormulaR1C1 = "=LEFT(RC[1],3)"
ActiveCell.Select
Selection.Copy
ActiveCell.Offset(0, 1).Select
ActiveCell.Offset(1, 0).Select
CheckCell:
ActiveCell.Activate
If ActiveCell.Value <> 0 Then
ActiveCell.Offset(0, -1).Select
ActiveCell.Activate
ActiveSheet.Paste
ActiveCell.Offset(0, 1).Select
ActiveCell.Offset(1, 0).Select
GoTo CheckCell
Else: GoTo EndOfData
End If
EndOfData:
End Sub
It's not elegant - it runs slower than a single select and paste - but it works, and it will work on all the files I need to process. Thanks again.