Copy and paste automatically on cell change using private sub - vba

I am trying to add an archive sheet to my workbook where closed tickets are collected. I would like the row of a particular ticket to be cut from a sheet labeled 'Tickets' and pasted into a sheet labeled 'Archive' once its status has changed from open to closed. I would like this to happen using a private sub so that it happens on cell change. The Status is found in column 4.
If that is possible im assuming it will be possible to do it the other way round too. So if a ticket is re-opened again and its status is changed in the 'Archive' sheet it will be cut and paste back into the 'Tickets' sheet.
This is the code we have so far. We can seem to get it to work. Any help would be greatly appreciated. Thank you
Private Sub Worksheet_Change (ByVal Target As Range)
If Target.Column = 4 Then
If Target.Value = "Closed" Then
R = Target.Row
Rows(R).Cut
Worksheets("Archive").Select
With ActiveSheet
lastrow = .Cells(.Rows.Count,"B").End(xlUp).Row
End With
Cells(lastrow,1).Select
Selection.Paste
End If
End If
End sub

Just making small amendments to your current code:
If Target.Column = 4 Then
If Target.Value = "Closed" Then
R = Target.Row
Rows(R).Cut
Worksheets("sheet3").Select
With ActiveSheet
lastrow = .Cells(.Rows.Count, "B").End(xlUp).Row + 1
.Cells(lastrow, 1).Select
.Paste
End With
End If
End If

Related

Push down values without shifting the row

So have this button on a form and I cant make the data on 3 cells (Ex:C1,D1,E1) go down 1 Row without shifting the whole sheet.
How do i make the values go down after being inserted so that i can leave Row1 allways open for new values?
This is the code on the command button, can i embed the solution on it or do I have to do it somewhere else?
Private Sub CommandButtonAddDes_Click()
Dim emptyRow As Long
'Activate sheet
Sheet1.Activate
'Determine emptyRow
emptyRow = WorksheetFunction.CountA(Range("C:C")) + 1
'Transfer info
Cells(emptyRow, 3).Value = Des
Cells(emptyRow, 4).Value = ComboBox.Value
Cells(emptyRow, 5).Value = TextBox1.Value
End Sub
Looks like you want to make sure that the cells being updated are then moved down automatically. You can do that with a change event on the sheet code behind like this:
Sub Worksheet_Change(ByVal Target As Range)
'Checks if the column being changed is C - E
If Target.Column = 3 Or Target.Column = 4 Or Target.Column = 5 Then
'Checks if the row being changed is row 2
If Target.Row = 2 Then
'Inserts a new row and pushes what is in C2:E2 down one row
Range("C2:E2").Insert Shift:=xlDown, CopyOrigin:=xlFormatFromLeftOrAbove
End If
End If
End Sub
This code will go in the sheets code behind. So, you will have to right-click on the sheet that you have this data on and then click on view code (or just double-click the sheet)
Putting the code there will tell Excel to watch that sheet for changes and then the code checks if it is happening to one of those 3 cells (C2:E2)
With data in C1, D1 and E1 just:
Range("C1:E1").Insert Shift:=xlDown
This pushes down the three cells rather than the entire row.
EDIT#1:
Put this event macro in the worksheet code area:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim c As Long
c = Target.Column
If c = 3 Or c = 4 Or c = 5 Then
If Application.WorksheetFunction.CountA(Range("C1:E1")) = 3 Then
Application.EnableEvents = False
Range("C1:E1").Insert shift:=xlDown
Application.EnableEvents = True
End If
End If
End Sub
Once you have entered all 3 values in C1 through E1, the values will get pushed down automatically.
Replace you code on your userform. Ensure your input box names are correct, i.e. ComboBox1, TextBox1, etc. I don't understand what "Des" is. If you need it in row 2 then just change the 1s to 2s.
Private Sub CommandButton1_Click()
With Sheets("Sheet1")
.Range("C1:E1").Insert Shift:=xlDown
.Range("C1").Value = Des.Value
.Range("D1").Value = ComboBox.Value
.Range("E1").Value = TextBox1.Value
End With
End Sub
Edited: changed lat column index from "D" to "E" and starting row index from "3" to "2"
Maybe you’re after this
Private Sub CommandButtonAddDes_Click()
With Sheet1
With .Range("C2:E" & .Cells(.Rows.Count, "C").End(xlUp).Row)
.Offset(1).Value = .Value
.Resize(1) = Array(Des, ComboBox.Value, TextBox1.Value)
End With
End With
End Sub
Should different rows need to be "inserted" you just change the row index.

VBA Worksheet change or calculate Event [duplicate]

I need help with an macro to notify me (by changing a cell background color to red), when the value (always number format) changes in any cells in the row. I want the background of cell E3 to change to red, if any of the values in cells F3:AN3 change from their current values.
The numbers in cells F3:AN3 will be entered manually or thru copy and paste of the row, and there won't be any formulas. Likewise, if any values in cells F4:AN4 are changed, I would like cell E4 to change to a red background, and so on for each of the rows in the chart. Not all rows will always have a value, so I would be looking for changes from "" to any #, or from one # to another #, or from any # to "". Ideally this would be an event macro that does not have to be run manually.
The following is the code I've started working with:
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("F3:AN3")) Is Nothing Then KeyCellsChanged
End Sub
Private Sub KeyCellsChanged()
Dim Cell As Object
For Each Cell In Range("E3")
Cell.Interior.ColorIndex = 3
Next Cell
End Sub
However, this macro seems to run regardless of whether the number in the cell is changed, as long as I press enter it highlight E3 as red.
Any help is much appreciated!
Here is my favorite way to detect changes in an Excel VBA app:
Create an exact copy of the range you're watching in hidden rows below the range the user sees.
Add another section below that (also hidden) with formulas subtracting the user range with the hidden range with an if statement that sets the value to 1 if the difference is anything but 0.
Use conditional formatting in the user range that changes the background color of the row if the corresponding change-detection row (or cell) is > 0.
What I like about this approach:
If a user makes a change and then reverts back to the original value, the row is "smart enough" to know that nothing has changed.
Code that runs any time a user changes something is a pain and can lead to problems. If you set up your change detection the way I'm describing, your code only fires when the sheet is initialized. The worksheet_change event is expensive, and also "may effectively turn off Excel’s Undo feature. Excel’s Undo stack is destroyed whenever an event procedure makes a change to the worksheet." (per John Walkenbach: Excel 2010 Power Programming)
You can detect if the user is navigating away from the page and warn them that their changes will be lost.
Depending on your answer to my question in the comments, this code may change. Paste this in the relevant Worksheet code area. For this to work, navigate to any other sheet and then navigate back to the original sheet.
Option Explicit
Dim PrevVal As Variant
Private Sub Worksheet_Activate()
If Selection.Rows.Count = 1 And Selection.Columns.Count = 1 Then
PrevVal = Selection.Value
Else
PrevVal = Selection
End If
End Sub
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
On Error GoTo ExitGraceFully
If Selection.Rows.Count = 1 And Selection.Columns.Count = 1 Then
PrevVal = Selection.Value
Else
PrevVal = Selection
End If
ExitGraceFully:
End Sub
Private Sub Worksheet_Change(ByVal Target As Range)
If Application.WorksheetFunction.CountA(Target) = 0 Then Exit Sub
Dim aCell As Range, i As Long, j As Long
On Error GoTo Whoa
Application.EnableEvents = False
If Not Intersect(Target, Columns("F:AN")) Is Nothing Then
If Target.Rows.Count = 1 And Target.Columns.Count >= 1 Then
Range("E" & Target.Row).Interior.ColorIndex = 3
ElseIf Target.Rows.Count > 1 And Target.Columns.Count = 1 Then
i = 1
For Each aCell In Target
If aCell.Value <> PrevVal(i, 1) Then
Range("E" & aCell.Row).Interior.ColorIndex = 3
End If
i = i + 1
Next
ElseIf Target.Rows.Count > 1 And Target.Columns.Count > 1 Then
Dim pRow As Long
i = 1: j = 1
pRow = Target.Cells(1, 1).Row
For Each aCell In Target
If aCell.Row <> pRow Then
i = i + 1: pRow = aCell.Row
j = 1
End If
If aCell.Value <> PrevVal(i, j) Then
Range("E" & aCell.Row).Interior.ColorIndex = 3
End If
j = j + 1
Next
End If
End If
LetsContinue:
Application.EnableEvents = True
Exit Sub
Whoa:
Resume LetsContinue
End Sub
SNAPSHOTS
It works as expected When you type a value in the cell. It also works when you copy 1 Cell and paste it in multiple cells. It doesn't work when you copy a block of cells and do a paste (I am still working on this)
NOTE: This is not extensively tested.

Excel VBA: How to create macro to change text color using if statement

This is a continuation for the following question: What is the cause for Conditional Formatting to get jumbled up?
In an attempt to prevent my conditional formatting from going haywire, I decided to convert it into code in VBA. I decided to start small and start with converting one conditional formatting into VBA.
Explanation:
In column O there are a series of numbers, obtained from a different sheet. User inputs number in column F. For example if number in F9 is less than O9, the font colour will become red. If not number remains normal. The formula should start at row 9 and can continue down onwards and should be automatic.
Meaning the moment a number is keyed in column F the font colour should change instantly.
The following is the code I created so far:
Sub change_color()
With Me.Range("f9", Range("f" & Rows.Count).End(xlUp)) 'so the formula will carry onwards from f9 onwards
If f9 < o9 Then
Range(f).Font.Color = vbRed
End If
End With
End Sub
But alas it didn't work. I also tried linking it to a button and nothing happens. And I also remember to remove my old conditional formatting as well. Is there something I'm missing?
You are after something like the code below.
This code is to be ran once, it will lopp through the entire column "F" in your worksheet, and change the font of all instances.
Regular Module Code
Option Explicit
Sub change_color()
Dim LastRow As Long, i As Long
With Worksheets("Sheet1") ' modify to your sheet's name
LastRow = .Cells(.Rows.Count, "F").End(xlUp).Row
For i = 1 To LastRow
If .Range("F" & i).Value < .Range("O" & i).Value Then
.Range("F" & i).Font.Color = vbRed
Else
.Range("F" & i).Font.Color = vbBlack
End If
Next i
End With
End Sub
To "catch" the modification in real-time, when someone changes a value in column "F", and then change the font according to the criteria you specified, you need add the following code to the Worksheet module, where you have your data, and add the piece of code below to Worksheet_Change event.
Code in Sheet1 module (modify to your sheet's)
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Column = 6 Then ' if someone changes a value in column "F"
Application.EnableEvents = False
If Target.Value < Range("O" & Target.Row).Value Then
Target.Font.Color = vbRed
Else
Target.Font.Color = vbBlack
End If
End If
Application.EnableEvents = True
End Sub
Does this work for you?
Option explicit
Sub ChangeColor()
With thisworkbook.worksheets(YOURSHEETNAME) 'Replace with sheet name as per your workbook.'
Dim LastRow as long
Lastrow = .cells(.rows.count,"F").end(xlup).row
Dim RowIndex as long
For rowindex = 9 to LastRow
If .cells(rowindex,"F").value2 < .cells(rowindex,"O").value2 then
.cells(rowindex,"F").font.color = vbred
End if
Next rowindex
End With
End Sub

MS Excel worksheet change event - keeping record of old cell value against new value

I'm new to this forum but have been building up my coding experience in the last couple of months due to the VBA requirements of my current role. Today's problem has seen me trawling through many sites (and my Excel VBA for Dummies book), but I haven't quite nailed it.
I am trying to make an audit tracker file in Excel for our company Risk Register. The idea is that once the risk register is established, any changes will create an audit trail (on a separate tab) which shows both the old and the new record.
I have written the code using the Change Event handler. I want my macro to fire every time there is a change and do the following:
1. Make a reference of the old cell value (what the user has just overwritten)
2. Jump to the 'Audit trail' tab and paste two copies of the full risk record - each risk record is a row of data that occupies 17 columns
3. In the first copy of these 17 columns, work out which column was edited and replace this cell with the old cell value (captured in step 1)
4. Insert a time stamp
5. Have conditional formatting highlight the record that has changed [this function is not required in the code as I've set it up within the spreadsheet itself]
6. Jump back to cell where the user just made their edit (on the 'Risk Register' tab)
I have managed steps 1, 2 and 4-7 but I am having problems getting the code to input the "old cell value" into the right spot in the 'Audit Tracker' tab. I can get it there if I manually define the cell range for it to paste into, but I can't seem to make it dynamic so that it will automatically recognize what field the user is changing and ensure the same field is amended in the audit trail.
Would really appreciate any insights as to why the "PasteRange.Value = Worksheets("Risk Register").Range("oldValuePaste")" line isn't working
My code is as follows:
Dim oldValue As Variant
Dim LastRow As Long
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Application.ScreenUpdating = False
If Not Intersect(Target, Range("b13:r13")) Is Nothing Then
oldValue = Target.Value
End If
Application.ScreenUpdating = True
End Sub
Private Sub Worksheet_Change(ByVal Target As Range)
Application.ScreenUpdating = False
If Not Intersect(Target, Range("b13:r14")) Is Nothing Then
If Target.Value <> oldValue Then
'MsgBox "You just changed " & Target.Address
Cells(65, 5).Value = oldValue 'this cell is a named range called: OldValuePaste
Cells(66, 5).Value = Target.row 'this cell is a named range called: OldValueRowNumber
Cells(67, 5).Value = Target.Column 'this cell is a named range called: OldValueColumnNumber
Range(Cells(Target.row, 2), Cells(Target.row, 18)).Copy
'Cells(70, 2).PasteSpecial xlPasteValues
Call Paste_on_AuditSheet
Sheets("Risk Register").Activate
Target.Select
Application.CutCopyMode = False
End If
End If
Application.ScreenUpdating = True
End Sub
_____________________________________________________________________________________________________
Sub Paste_on_AuditSheet()
Application.ScreenUpdating = False
Dim LastRow As Long
Dim ColNum As Long
Dim PasteRange As Range
ColNum = OldValueColumnNumber
Sheets("Audit trail").Select
'MsgBox "Activated " & ActiveSheet.Name
'Find the last used row in a Column: column B in this example
With ActiveSheet
LastRow = .Cells(.Rows.Count, "B").End(xlUp).row
End With
Set PasteRange = Cells(LastRow, ColNum)
'The following two lines bring in the new data and paste into old record and new record sections:
Cells(LastRow + 1, 2).PasteSpecial xlPasteValues
Cells(LastRow + 1, 20).PasteSpecial xlPasteValues
'Then this line goes back over the piece just pasted in and changes one cell in "old record" section to what it was prior to the edit:
'PasteRange.Value = Worksheets("Risk Register").Range("oldValuePaste")
'Above line of code is not working, but can get it to do the right thing using this code (although it's not dynamic):
Range("E3").Value = Worksheets("Risk Register").Range("oldValuePaste")
'Add a time stamp:
Cells(LastRow + 1, 1) = Now
Application.ScreenUpdating = True
End Sub
One last point - despite my repeated use of Application.ScreenUpdating commands, I still get some screen flashing - any ideas why?
Thanks in advance for the help!
In reviewing your code, I saw a few things that I didn't think would work as you supposed they would, and also recognized that your code could be made much simpler and just be called from the Worksheet_Change event.
So the refactored code below and let me know if you have issues:
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("b13:r14")) Is Nothing Then
'get oldValue
Dim sNewVal As String, sOldVal As String
sNewValue = Target.Value 'store current or "new" value since this is what is stored after the cell change takes place
With Application
.EnableEvents = False 'turns off event firing so the code will not go into endless loop
.Undo 'undo the change (to store old value in next line)
End With
sOldValue = Target.Value 'store old value
Target.Value = sNewValue 'reset new value
Dim lCol As Long
lCol = Target.Column 'which column of data was changed
'assumes columns A-Q are 17 columns
Me.Range(Me.Cells(Target.Row, 1), Me.Cells(Target.Row, 17)).Copy
With Sheets("Audit Trail")
Dim lRow As Long
lRow = .Range("B" & .Rows.Count).End(xlUp).Offset(1).Row
.Range("B" & lRow).PasteSpecial xlPasteValues
.Range("B" & lRow + 1).PasteSpecial xlPasteValues
.Range("A" & lRow).Value = Now
.Cells(lRow, lCol + 1).Value = sOldValue 'store old value in first pasted line ... add 1 since starting from column B
End With
End If
Application.EnableEvents = True
End Sub

How to execute Macro code inside a module from a worksheet_change event

I have a set of raw data on sheet 1 of my workbook. On sheet 2 I use formulas to pull in some of that data from sheet 1.
Using a Macro that I created and posted in Module 1 I want to hide any rows that do not contain specific data. I am able to execute the macro directly when I need to via Run>Run Sub/Userform. It works perfectly.
However, I would prefer it to run when it needs to update via a worksheet_change event in the background whenever an edit is made to sheet 1. Because I am making edits on sheet 1 but want the change to execute the macro on sheet 2 I understand that the worksheet_change event has to be placed in "This Worksheet" as opposed to the specific sheet.
Here's the macro code
Sub HideRows()
Dim i As Integer
i = 1
Do While Not Cells(i, 5) = ""
If Cells(i, 5).Value = 0 Then
Rows(CStr(i) + ":" + CStr(i)).EntireRow.Hidden = True
ElseIf Cells(i, 5).Value <> 0 And Rows(CStr(i) + ":" + CStr(i)).EntireRow.Hidden = True Then
Rows(CStr(i) + ":" + CStr(i)).EntireRow.Hidden = False
End If
i = i + 1
Loop
End Sub
Run directly the code above does what I need. The code I am using below to execute this via a worksheet_change event doesn't work.
Private Sub Worksheet_Change(ByVal Target As Range)
With Me.Worksheets("Sheet2")
Call HideRows
End With
End Sub
Any help with how to execute the macro using worksheet_change would be appreciated.
Few points worth noting
The problem is that you are not fully qualifying the cells so when the HideRows macro is called, even though you have used With Me.Worksheets("Sheet2") it is still referring to the current sheet which is Sheet1. Fully qualify your range objects as shown below. Notice the Dots before them?
If the changes are happening in Col A of Sheet1 then trap that else your macro will run for any change in Sheet1 thereby making your workbook slow.
You do not need to keep the macro in a module. You can put the entire code in Sheet1 as shown below
Also Rows(CStr(i) + ":" + CStr(i)) can be written as Rows(i)
When working with rows in Excel it is always advisable to declare them as Long and not Integer. Post Excel2007 the number of rows have increased and the Integer variable may not be able to accommodate that.
Is this what you are trying? Put this code in Sheet code area of Sheet1
Private Sub Worksheet_Change(ByVal Target As Range)
Dim i As Long, lRow As Long
'~~> Check of the change is happening in Col 1
If Not Intersect(Target, Columns(1)) Is Nothing Then
With Worksheets("Sheet2")
lRow = .Range("E" & .Rows.Count).End(xlUp).Row
For i = 1 To lRow
If .Cells(i, 5).Value = 0 Then
.Rows(i).EntireRow.Hidden = True
ElseIf .Cells(i, 5).Value <> 0 And .Rows(i).EntireRow.Hidden = True Then
.Rows(i).EntireRow.Hidden = False
End If
Next i
End With
End If
End Sub