Excel VBA Copy Paste issue - vba

I am working with Excel VBA Copy Paste. Cell R7 has formula =Max ("C77:AD81").
R7 = Highest Value for Month
F7 = Highest Value to date
Q7 = the date F7 was achieved
What I am trying to achieve is if R7 > F7, copy R7 Value to F7 and change the Q7 to = today.
All I'm achieving is R7 changes to max of ("C77:AD81") and the remaining code doesn't work. My code below.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rng As Range, r As Range, rv As Long
If Not Intersect(Target, Range("R7")) Is Nothing Then
Set rng = Intersect(Target, Range("R7"))
For Each r In rng
'Change Best Peak Flow and Date Achieved
Select Case r.Value
Case Is > ("F7")
Case Range("R7").Select
Case Range("R7").Copy
Case Range("F7").Select
Case Range("F7").Paste
Case ("R7") = ("F7")
Case Range("Q5").Select
Range("Q5") = Today()
Application.CutCopyMode = False
End Select
Next r
End If
End Sub

My advice is not to use .select. You can program everything without a single .select. Recording and analyzing macros are very good starting point for learning VBA, but sometimes they are way too complicated. I prefer simple solutions so give this a try:
Private Sub Worksheet_Change(ByVal Target As Range)
If Range("F7") <> Range("R7") Then
Range("F7") = Range("R7")
Range("Q5") = Date
End If
End Sub

So, your rng object is only 1 cell, because you specified 1 target range of R7. With this being said, your For Each...Next statement is redundant.
I also wouldn't even use Select Case at all, but I will leave it in the event you later want to build off of it.
Give this a shot
Private Sub Worksheet_Change(ByVal Target As Range)
On Error GoTo ErrHandler 'Important to ensure events are reenabled
Application.EnableEvents = False
Dim rng As Range, r As Range, rv As Long
Set rng = Intersect(Target, Range("R7"))
If Not rng Is Nothing Then
'Change Best Peak Flow and Date Achieved
Select Case True
Case r.Value > Range("F7").Value
Range("F7") = Range("R7")
Range("Q5") = Date
End Select
End If
Application.EnableEvents = True
Exit Sub
ErrHandler:
Application.EnableEvents = True
MsgBox Err.Number & vbNewLine & Err.Description
End Sub

I solved it.
Here is the code I used.
Private Sub Worksheet_Change(ByVal Target As Range)
'Change Best Peak Flow and Date Achieved
If Range("R7").Value > Range("F7").Value Then
Range("R7").Select
Selection.Copy
Range("F7").Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Range("Q5").Select
Selection.Copy
Range("K7").Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Application.CutCopyMode = False
End If
End Sub

Related

Copying a range of formulas and pasting values to a selected row

I'm trying to create a button that will copy a range of formulas and pasted their values starting in the selected cell.
My formula range is from U1:EN1. I'd like to paste these values into and to the right of whichever cell is selected when the "Get New Data" button is clicked.
Below is what I have so far:
Sub Update_Quote_Data_5()
'
' Update_Quote_Data_5 Macro
'
'
Range("U1").Select
Range(Selection, Selection.End(xlToRight)).Select
Selection.Copy
ActiveWindow.LargeScroll ToRight:=-5
Range("U9").Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Application.CutCopyMode = False
Range("U10").Select
End Sub
You may also try something like this...
Sub Update_Quote_Data()
Dim Rng As Range
Set Rng = Range("U1:EN1")
ActiveCell.Resize(1, Rng.Columns.Count).Value = Rng.Value
End Sub
Start with this:
Sub Update_Quote_Data_5_The_Sequel()
Dim r1 As Range, r2 As Range
Set r1 = Range("U1:EN1")
Set r2 = ActiveCell
r1.Copy
r2.PasteSpecial xlPasteValues
End Sub
EDIT#1:
To move to the cell below the previous ActiveCell:
Sub Update_Quote_Data_5_The_Sequel()
Dim r1 As Range, r2 As Range
Set r1 = Range("U1:EN1")
Set r2 = ActiveCell
r1.Copy
r2.PasteSpecial xlPasteValues
r2.Offset(1, 0).Select
End Sub

Paste Special Not Working more than 1 time: 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

Excel VBA - Run a macro based on a range of dropdown lists

It's been a few years since VBA class so please respond as if you were writing in an "Excel VBA for Dummies" book.
In column G, each cell in range G2:G1001 is an individual data validation drop down list of all the worksheets in my workbook. I have a macro that when you select "Questar" from the dropdown in cell "G2", it copies cells A2:F2 and pastes them to the worksheet titled "Questar" in the first empty row. That all works fine.
However, my issue is it only works in cell G2. I have data in rows 2-1001 and I need this to work for all cells G2:G1001. Here is what I have so far and works for cell "G2":
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("G2:G1001")) Is Nothing Then
Select Case Range("G2")
Case "Questar": Questar
End Select
End If
End Sub
I think that the Select Case Range("G2") needs to change but I have tried everything.
Here is my Questar macro code:
Sub Questar()
Worksheets("AFCU Auto-Add").Range(ActiveCell.Offset(0, -6), ActiveCell.Offset(0, -1)).Copy
Worksheets("Questar").Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).PasteSpecial xlPasteValues
Application.CutCopyMode = False
Application.ScreenUpdating = True
Sheets("AFCU Auto-Add").Select
Range(ActiveCell.Offset(0, -6), ActiveCell.Offset(0, -1)).Select
Application.CutCopyMode = False
Selection.ListObject.ListRows(1).Delete
Range("G2").Select
End Sub
I will eventually add more cases but I want to get one worksheet working correctly before adding more cases and macros. Any suggestions?
EDIT: updated to single procedure, assuming all sheets exist which are named in column G...
Something like:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rng As Range, c As Range, rngDel As Range
On Error GoTo haveError
Set rng = Intersect(Target, Range("G2:G1001"))
If Not rng Is Nothing Then
For Each c In rng.Cells
If Len(c.Value) > 0 Then
'copy to appropiate sheet
With ThisWorkbook.Worksheets(c.Value).Cells(Rows.Count, 1).End(xlUp)
.Offset(1, 0).Resize(1, rng.Cells.Count).Value = _
c.EntireRow.Range("A1:F1").Value
End With
'build up a range of rows to delete...
If rngDel Is Nothing Then
Set rngDel = c
Else
Set rngDel = Union(c, rngDel)
End If
End If
Next c
'any rows to delete?
If Not rngDel Is Nothing Then
Application.EnableEvents = False
rngDel.EntireRow.Delete
Application.EnableEvents = True
End If
End If
Exit Sub
haveError:
'make sure to re-enable events in the case of an error
Application.EnableEvents = True
End Sub

Calling(run) a private Sub worksheet_Change(ByVal Target As Range) from public sub

I am wondering if it is possible to call a private Sub worksheet_Change(ByVal Target As Range) type of sub from another public sub? I know that you can't really 'call' the sub but Run it, however my attempts at running the sub doesn't seem to work. This is what I have tried:
Sub AccessTransfer()
Range("A1:F1").Select
Selection.Copy
Sheets("Sheet2").Select
ActiveSheet.Paste
ActiveCell.Offset(0, 6).Value = "Oven"
Range("A65536").End(xlUp).Offset(1, 0).Select
Run.Application "Private Sub Worksheet_Change(ByVal Target As Range)"
Sheets("Sheet1").Select
Application.CutCopyMode = False
End Sub
Private Sub Worksheet_Change(ByVal Target As Range)
If Application.CountIf(Range("A:A"), Target) > 1 Then
MsgBox "Duplicate Entry", vbCritical, "Remove Data"
Target.Value = ""
End If
Range("A65536").End(xlUp).Offset(1, 0).Select
End Sub
Any help or suggestions on how to fix my problem would be most appreciated.
With Sheets("Sheet2").Range("A65536").End(xlUp).Offset(1, 0)
.Value = .Value
End With
will trigger the Event, but the Paste should already have done that...
EDIT: As commenters have pointed out, there are other issues with your code: this should be something like what you want to do -
Sub AccessTransfer()
Dim shtSrc As Worksheet, shtDest As Worksheet
Dim v, c As Range
Set shtSrc = ActiveSheet
Set shtDest = ThisWorkbook.Sheets("Sheet2")
v = shtSrc.Range("A1").Value 'value to check...
If Application.CountIf(shtDest.Range("A:A"), v) > 0 Then
MsgBox "Value '" & v & "' already exists!", vbCritical, "Can't Transfer!"
Else
'OK to copy over...
Set c = shtDest.Range("A65536").End(xlUp).Offset(1, 0)
shtSrc.Range("A1:F1").Copy c
c.Offset(0, 6).Value = "oven"
End If
Application.CutCopyMode = False
End Sub
There are a couple of things wrong with your code.
You may be making a change (e.g. Target.Value = "") in the Worksheet_Change which will trigger another event.
You haven't isolated Target to column A and have not dealt with more than a single cell being Target.
Module1 code sheet:
Sub AccessTransfer()
With Worksheets("Sheet2")
Worksheets("Sheet1").Range("A1:F1").Copy _
Destination:=.Cells(.Rows.Count, "A").End(xlUp).Offset(1, 0)
'Sheet2's Worksheet_Change has been triggered right here
'check if the action has been reversed
If Not IsEmpty(.Cells(.Rows.Count, "A").End(xlUp)) Then
'turn off events for the Oven value write
Application.EnableEvents = False
.Cells(.Rows.Count, "A").End(xlUp).Offset(0, 6) = "Oven"
'turn events back on
Application.EnableEvents = True
End If
End With
End Sub
Sheet2 code sheet:
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("A:A")) Is Nothing Then
On Error GoTo bm_Safe_Exit
Application.EnableEvents = False
Dim c As Long, rngs As Range
Set rngs = Intersect(Target, Range("A:A"))
For c = rngs.Count To 1 Step -1
If Application.CountIf(Columns("A"), rngs(c)) > 1 Then
MsgBox "Duplicate Entry in " & rngs(c).Address(0, 0), _
vbCritical, "Remove Data"
rngs(c).EntireRow.Delete
End If
Next c
End If
bm_Safe_Exit:
Application.EnableEvents = True
End Sub

Worksheet_SelectionChange - Timestamp in different column

I am using the code below to time stamp my employees priority list when they change a task to Completed. The code works fine but has to be replicated for each cell that I want to track the changes in.
Ideally, I would like the code to have the exact same functionality but compressed so that I can have it look at a large range, M5:M2500, and if cell M250 is changed to Completed have it look through Y5:Y500 and paste the time stamp in cell Y250.
Hopefully this make sense and thanks for any suggestions!
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Address = "$M$5" Then
Call Complete5
End If
If Target.Address = "$M$6" Then
Call Complete6
End If
End Sub
Sub Complete5()
ActiveSheet.Unprotect Password:="unlock"
If InStr(1, Range("$M$5"), "Completed") > 0 Then
Range("$Y$5").Select
ActiveCell.FormulaR1C1 = "=NOW()"
ActiveCell.Select
Selection.Copy
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Application.CutCopyMode = False
Range("$M$5").Select
Else
Range("$Y$5").Select
ActiveCell.ClearContents
Range("$M$5").Select
End If
End Sub
Sub Complete6()
ActiveSheet.Unprotect Password:="unlock"
If InStr(1, Range("$M$6"), "Completed") > 0 Then
Range("$Y$6").Select
ActiveCell.FormulaR1C1 = "=NOW()"
ActiveCell.Select
Selection.Copy
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Application.CutCopyMode = False
Range("$M$6").Select
Else
Range("$Y$6").Select
ActiveCell.ClearContents
Range("$M$6").Select
End If
End Sub
You can do this very cleanly right within the Worksheet_Change event itself. This code will evaluate the row in M that was changed and modify the corresponding row in Y accordingly and will also work if a user marks several rows complete at the same time (Ctrl + Enter). Warning, it will not fire when a user pastes a value into the cell.
Also, pay close attention to how I removed all the .Select and .Activate statements and worked directly with the objects themselves.
Private Sub Worksheet_Change(ByVal Target As Range)
With Me
If Not Intersect(Target, .Range("M5:M2500")) Is Nothing Then
Application.EnableEvents = False
.Unprotect Password:="unlock"
Dim rng As Range, cel As Range
Set rng = Target
For Each cel In rng
If InStr(1, cel, "Completed") Then
'use offset of 12 columns to get to column "Y"
cel.Offset(, 12).Value = Now
Else
cel.Offset(, 12).ClearContents
End If
Next
Application.EnableEvents = True
End If
'.Protect Password:="unlock"
End With
End Sub