update cell and paste it to another cell vba - vba

I am quite new in excel vba and I would really appreciate if you can assist me.
The thing is that I have cell which updates each minute because it is linked with a function to Blomberg. The thing is that I want that each time cell updates excel copies it and pastes to another, new cell that i can observe the intra day changes.
I have come up with some codes but I can copy and paste only to one, similar cell.It looks like following:
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Cells.Count > 1 Then Exit Sub
If Not Intersect(Target, Range("E4")) Is Nothing Then
Range("E4").Copy
Range("E4").PasteSpecial xlPasteValues
End If
End Sub
Any help would be highly appreciated.

If I understand your problem correctly you want to copy the value to a new cell, for logging purposes? What I would do in this case is have another sheet for logging the values named "logger_sheet" I paste a value in cell a1 when the blomberg cell updates, copy the value into my logger_sheet cell a2 when it changes copy it to a3 then a4 etc.
Here is your updated code. It assumes you have a sheet named "logger_sheet" (if you dont have one, create it) to store all the previous values. When the blomberg cell updates, it copies the value and pastes it to the next avaliable logging_sheet cell. I have developed a function that finds the last used row in a specified sheet and column. Try it out
Also there is a line you can uncomment if you want to prevent excel from flashing, I labeled it in the code
Sub Worksheet_Change(ByVal Target As Range)
If Target.Cells.Count > 1 Then Exit Sub
target_cell = "E4"
col_to_log_data = "A"
logging_Sheet = "logger_sheet"
If Not Intersect(Target, Range("E4")) Is Nothing Then
'uncomment this line to stop the "flashing"
'Application.ScreenUpdating = False
'gets the name of the current sheet
data_sheet = Range(target_cell).Parent.Name
Range(target_cell).Select
Selection.Copy
'gets the next free row from column a of the logging sheet (the next free row is
'the last used row + 1)
next_free_row = GetLastRowByColumn(CStr(col_to_log_data), CStr(logging_Sheet)) + 1
'pastes the value
Sheets(logging_Sheet).Range(col_to_log_data & CStr(next_free_row)).PasteSpecial Paste:=xlPasteValues
Application.CutCopyMode = False
'switches back to the data sheet
Sheets(data_sheet).Select
'make sure you turn screen updating on (if it was never off it still works)
Application.ScreenUpdating = True
End If
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'this finds the last row in a specific column
'PARAMS: col_to_check, the clumn we want the last row of
' Opt: sheet_name, the sheet you want to check last row of
' default is current sheet if not specified
'RETURN: the last row number used in the sheet
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function GetLastRowByColumn(col_to_check As String, Optional sheet_name As String)
'gets current sheet name
the_current_sheet = ActiveSheet.Name
'if the user didnt' specify a sheet use the current one
If (Len(sheet_name) = 0) Then
sheet_name = the_current_sheet
End If
'gets last row
GetLastRowByColumn = Sheets(sheet_name).Range(col_to_check & "65536").End(xlUp).Row
'returns to original sheet
Sheets(the_current_sheet).Select
End Function
If my answer solves your problem please mark it as the solution

How about this? It will transfer E4 to Sheet2 in a new row each time E4 changes.
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Cells.Count > 1 Then Exit Sub
If Target.Address = "$E$4" Then Sheets("Sheet2").Cells(Rows.Count, "F").End(xlUp).Offset(1) = Target
End Sub

I'm making the assumption you want to log every change of values.
I would advise to keep a log in a separate sheet. Let's call it LogSheet.
Sub WriteLog(ByRef r As range)
Dim Lastrow as integer
With ThisWorkBook.WorkSheets("LogSheet")
LastRow = .Cells(.Rows.Count,"A").End(XlUp).Row
.Range("A" & LastRow + 1).Value = Now & " - " & r.Value
End With
End Sub
This sub will basically write all changes in column A of our log sheet with a timestamp!
Now, we need to make changes to your code in order to tell, to make logs whenever there is a change. To do so, we're going to make a call to our function and tell to copy the content of the range("E4") (The one that gets updated all the time)
If Not Intersect(Target, Range("E4")) Is Nothing Then
'add this line
WriteLog(ActiveSheet.Range("E4"))
Try it now.

Related

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.

VBA if cell equals certain value make that cell next available cell

I have a sheet called "JE", and in column C (C7:C446) users are able to populate the cells with account codes by two methods:
Double-clicking codes on a separate sheet called acct_codes
Manually enter the codes in column C. When the user manually enters the account code in column C, it is checked and changed to #N/A if it isn't listed in the acct_codes sheet to indicate to the user the code they entered was incorrect.
Column E is an account description column it will show the description associated with the account code in column C.
As it is now, the next available cell is the next blank cell in column C. I would like to make any cell in column E that equals #N/A to be the next available cell, THEN the next blank cell can be the next available cell.
For example if cell E11 has a value of #N/A, then the next time a user navigates to the acct_codes sheet and double-clicks a valid account code, I would like the account code they clicked to overwrite and populate C11.
I am unsure of the syntax to accomplish this and am having a hard time finding good example of this online. If anyone knows of a way I can go about doing this, I'd greatly appreciate it.
Here is the code I have in the acct_codes sheet:
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
Dim acctDesc As Range
Set acctDesc = Range("E7:E446")
If Target.Column = 1 Then
For j = 7 To 447
If Worksheets("JE").Range("C" & j).Value = "" Then
Worksheets("JE").Range("C" & j).Value = ActiveCell.Value
Worksheets("JE").Activate
Exit For
End If
Next j
End If
For Each Cell In acctDesc
If Cell.Value = "#N/A" Then
'Make that next available cell'
'else make next blank cell next available'
End If
Next Cell
Cancel = True
End Sub
EDIT
Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
Dim NextAvailableCell As Range
With ThisWorkbook.Sheets("JE")
Set NextAvailableCell = .Range("C7:C447").Find(What:="#N/A", _
LookAt:=xlWhole, _
LookIn:=xlValues)
If NextAvailableCell Is Nothing Then
Set NextAvailableCell = .Range("C7:C448").End(xlUp).Offset(1, 0)
.Cells(NextAvailableCell.Row, "C").Value = Target.Value
End If
End With
NextAvailableCell.Value = Target.Value
Cancel = True
Call Back_to_JE 'calls a macro that brings user back to main form/sheet "JE"
End Sub
It would be much quicker to use Find
Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
Dim NextAvailableCell As Range
With ThisWorkbook.Sheets("JE")
' Set equal to next "#N/A" cell
Set NextAvailableCell = .Range("C7:C447").Find(What:="#N/A", _
LookAt:=xlWhole, _
LookIn:=xlValues)
' If no "#N/A" cell was found then get last empty cell in column,
' assuming it's above C448
If NextAvailableCell Is Nothing Then
Set NextAvailableCell = .Range("C448").End(xlUp).Offset(1, 0)
End If
End With
' Assign value of double clicked cell
NextAvailableCell.Value = Target.Value
' Make it so that the double clicker doesn't enter the cell
Cancel = True
End Sub
This also avoids activating and selecting, which is good practise.
I'm unsure which cell you want to write to. If you want to search for #N/A in column E instead of column C, simply change that in the .Range(___).Find line. Ditto for the next blank cell in the relevant line.
If you want to write to a specific column, say column E, then use something like
' Inside the With block for ThisWorkbook.Sheets("JE")
.Cells(NextAvailableCell.Row, "E").Value = Target.Value

combining macros in an excel worksheet

I'm attempting to create a worksheet macro that will populate specific cells with default values in the same row when a value is entered in the first column of the row and also copy an entered value from the same row into other cells in that row. For example, when the user enters some value in 2A, cells 2C and 2D automatically populate with the numbers 10 and 20 respectively. Then, when the user enters a value in 2S, that same value is automatically copied back to cells 2I and 2J.
Thanks for the additional info Ralph. Based off of what I've found through researching similar questions on stackoverflow and general internet searches, I put together the following:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim A As Range, S As Range, InteA As Range, InteS As Range, r As Range
Set A = Range("A:A")
Set S = Range("S:S")
Set InteA = Intersect(A, Target)
Set InteS = Intersect(S, Target)
Application.EnableEvents = False
If Not InteA Is Nothing Then
For Each r In InteA
r.Offset(0, 2).Value = "10"
r.Offset(0, 3).Value = "20"
Next r
ElseIf Not InteS Is Nothing Then
For Each r In InteS
r.Offset(0, -9).Value = Target
r.Offset(0, -10).Value = Target
r.Offset(0, -11).Value = Target
Next r
End If
Letscontinue:
Application.EnableEvents = True
Exit Sub
Whoa:
MsgBox Err.Description
Resume Letscontinue
End Sub
To get a macro to run, an event of some kind has to occur. Its tempting to try to run a macro whenever ANY change is made to the worksheet, but imagine how often that's going to trigger? All the time. Then you have to worry if 10 & 20 will start flying into those cells when you don't want them to and write some conditional code to skip the process if you aren't typing in column A...
So here's a different option you might prefer. Enter formulas in columns C and D that will result in 10 & 20 if data exists in A.
=IF(A2<>"",10,"") or =IF(ISNUMBER(A2),10,0) ...whatever you like.
Then select your header row and data row, convert to an real "Excel table" on the Insert menu. (Insert...Table) This will extend your formulas to new rows as you type into column A.
Macro averted?

VBA Macro to Autofill a cell

I am trying to find a simple autofill solution to copy the formula in cell C3 into C2 after a new line has been inserted. Here is what I have that I thought would work:
Sub AutoFill()
Set SourceRange = Worksheets("Sheet 1").Range("C3")
Set fillRange = Worksheets("Sheet 1").Range("C2")
SourceRange.AutoFill Destination:=fillRange
End Sub
Basically, in C3 (and every cell in column C after row 3) has a average function that takes the previous 20 days and creates an average. I am trying to get the macro to input that formula everytime a new row gets put in (I have the code to input the new row it just won't apply the function after the new row comes in)
The cells to be filled. The destination must include the source range.
As quoted from MSDN.
So try:
Set SourceRange = Worksheets("Sheet 1").Range("C3")
Set fillRange = Worksheets("Sheet 1").Range("C2")
SourceRange.AutoFill Destination:=Range(fillRange, SourceRange)
Another note is to use Named ranges if you are inserting rows in between.
Other ways to get formulas with updated cell references
Option Explicit
Public Sub getFormula()
With Sheet1
.Range("C3").Copy
.Range("C2").PasteSpecial xlPasteFormulas
If .ListObjects.Count = 1 Then
With .ListObjects(1) 'for tables
.Cells(2, 3).Formula = .Cells(3, 3).Formula
End With
End If
End With
End Sub
.
Also, you should not use the name of VBA method as a sub name (AutoFill)
A fast way to determine VBA keywords: click on the sub name and press F1
If the help page shows Keyword Not Found your sub name should be Ok

Change cells to the right for as much as the current cell was changed

I have a train arrival/departure timetable, each branch line is on a separate row. I need to make the spreadsheet automatically detect change in cell and change all cells to the right from it for the same amount of time. Cells have format of time. How do I do that?
What I've tried so far:
I found this piece of code in some other question on SE:
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Target.Worksheet.Range("H5")) Is Nothing Then Macro
End Sub
But I wasn't able to understand where to put it and how to make it work automatically, apart from working for a specific range, which might be different from what was in the question where I found it.
UPD: The logic I'm looking for in VBA:
Wait until a cell is selected, if it has Time format, copy its value to Tmp.
Save the difference between old and new values to Tmp.
If a cell to the right contains something and its format is Time, add Tmp to it.
Continue until cell is empty.
if you know how much difference is between times, then you can calculate the new time based on that difference.
e.g. A1 is 10:07, A2 is 10:14. Instead of having to type in each time individually, you could have A2 as =A1+TIME(0,7,0). Then when you changed A1 to 10:15, A2 would automatically change to 10:22
OK this may get you started in the right direction:
You first want to store all the original cell values. So, the following VBA code stores the values in column A for the first 200 rows into an array. You need to run this code first, perhaps when the workbook is opened:
Dim contents(200) As Variant
Public Sub StoreOriginalValues()
' save all existing values
For r = 1 To 200 ' change for number of rows you have
contents(r) = Worksheets(1).Cells(r, 1).Value
Next
End Sub
Once the original cell values are stored, you can place code in the Worksheet_Change event so that whenever the user changes a cell in column A you can compare the original and new value and figure out the difference. Then you can apply this difference to the rest of the columns in that row:
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Column = 1 Then ' only check for changes in column A
originalvalue = contents(Target.Row)
newvalue = Target.Value
contents(Target.Row) = Target.Value
difference = newvalue - originalvalue
Set chgcell = Cells(Target.Row, Target.Column + 1)
Do While Not IsEmpty(chgcell)
chgcell.Value = chgcell.Value + difference
Set chgcell = chgcell.Offset(0, 1) ' move one column to right
Loop
End If
End Sub
Now this code is by no means perfect. It does not check that they entered a valid time, for instance. It also does not check to see if the values entered in the rest of the columns in the row are times or text or whatnot. But like I said I hope it will point you in the right direction.
My solution:
Dim oldVal
Dim diff
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
oldVal = Target.Value
End Sub
Private Sub Worksheet_Change(ByVal Target As Range)
Application.EnableEvents = False
ActiveCell.Offset(-1, 0).Select
Application.EnableEvents = True
diff = Target.Value - oldVal
If Not diff = 0 Then
While Not ActiveCell.Offset(0, 1) = "#"
Application.EnableEvents = False
ActiveCell.Offset(0, 1).Select
Application.EnableEvents = True
If Not ActiveCell Is Nothing _
And Not ActiveCell = "" _
And TypeName(ActiveCell) = TypeName(ActiveCell.Offset(0, -1)) Then
Application.EnableEvents = False
ActiveCell.Value = ActiveCell.Value + diff
Application.EnableEvents = True
End If
Wend
End If
End Sub
This has been my first experience with VB in a very long time, so the code is terrible, but it works.