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

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.

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.

Cell Interior Color Index Excel VBA

Based on a language table, column A = Language, B = number, C = coloredcell
I would like to know what is the VBA so whenever I type a number on Column B (using Workbook_SheetChange), C is colored with the Colorindex equal to the number typed.
On the other hand, and I am sure is part of the solution to the previous question, on VBA how do I write cell.Interior.ColorIndex = (a specific cell value, If B2=4 -> for the row, whole or until last column has data, cell.Interior.ColorIndex = 4) and color the whole row.
Thank you
The sheetchange function has target as an argument, that's the cell that you changed. You can use it to change the relevant cell:
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Column = 2 Then
Target.Offset(0,1).Interior.ColorIndex = Target.Value
'and for the whole row
Target.EntireRow.Interior.Color = Target.Offset(0,1).Interior.Color
Endif
End Sub
The code of Nick Dewitt is OK, but it color only the column C.
If you want to color the entire row, starting from C depending of how much columns are in the row :
Private Sub Worksheet_Change(ByVal Target As Range)
Dim lastcol As Integer, i As Integer
If Target.Column = 2 Then
lastcol = Cells(Target.Row, Columns.Count).End(xlToLeft).Column
For i = 3 To lastcol
Target.Offset(0, i - 2).Interior.ColorIndex = Target.Value
Next i
End If
End Sub
Right click on the sheet's name on which you want this functionality, and click on 'View Code'.
Now you need to write a VBA function that fires on any change to the worksheet. This is an inbuilt function called Worksheet_Change(Range). The range object (it's argument) is the range that had changed when this function fired.
Private Sub Worksheet_Change(ByVal Target As Range)
End Sub
Inside the function you need to check whether the changed cell was in column B. This is done by the Column property of the Target range.
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Column = 2 Then
' The changed cell was in column B
End If
End Sub
Now you need to get the cell's value and put it as the row's ColorIndex.
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Column = 2 Then
ColorValue = Target.Value
Target.EntireRow.Interior.ColorIndex = ColorValue
End If
End Sub
Edit: To color the cells only till the last value in the row, you need to count the number of filled cells in the row. The following code does that (see the comments to understand it)
Private Sub Worksheet_Change(ByVal Target As Range)
' Check if the edited cell is in column B
If Target.Column = 2 Then
' Get the value of the edited cell
ColorValue = Target.Value
' Get the row number of the edited cell
RowNumber = Target.Row
' Get the number of filled cells in this row
CellsCount = Application.WorksheetFunction.CountA(Range(RowNumber & ":" & RowNumber))
' Apply the color formatting till the last column in this row
Range(Cells(RowNumber, 1), Cells(RowNumber, CellsCount)).Interior.ColorIndex = ColorValue
End If
End Sub

Excel 2007 VBA Auditing

New to VBA
thanx in advance..
is it possible to automatically undo changes to a row range depending on another corresponding cell value?
for example :
cells A2,B2,C2 are the cells that user inter data within
Cell G2 the cell which the auditor Approve the interned data on the raw by typing "yes"
so if G2 value is "yes" any change to the values in A2,B2,C2 is canceled "undo" and return to its original data,
if G2 is not "yes" then user can alter the value in cells A2,B2,C2 as he wants
and that goes for the other cells in sequnce A3,B3,C3 versus G3, A4,B4,C4 versus G4 and so on...
Code copied from comment:
Private Sub Worksheet_Change(ByVal Target As Range)
On Error GoTo er1
Application.EnableEvents = False
If Not Intersect(Target, Range("A1:d10")) Is Nothing Then
If Target.Range("g1:g10").Value = "Yes" Then
Application.Undo
Else
End If
End If
err2:
Application.EnableEvents = True
Exit Sub
er1:
MsgBox Err.Description
Resume err2
End Sub
The problem is in the code where you are trying to check if column G is "Yes":
If Target.Range("g1:g10").Value = "Yes" Then
Application.Undo
Else
End If
The Target variable is already a range and will not be column G if the user has entered something in columns A-D. Instead, you must work out the row number which has just been changed and then look at column G for that row. Replace your block of code with this:
Dim rowNumber As Long
' This **assumes** only 1 cell has been changed
rowNumber = Target.Row
If Target.Parent.Cells(RowIndex:=rowNumber, ColumnIndex:="G").Value = "Yes" Then
Application.Undo
Else
End If
Warning This code assumes that only one cell has been changed. If you think that Target might be more than one cell, you should loop through the cells checking them all:
Dim rowNumber As Long
Dim cell As Range
For Each cell In Target.Cells
rowNumber = cell.Row
If Target.Parent.Cells(RowIndex:=rowNumber, ColumnIndex:="G").Value = "Yes" Then
Application.Undo
Exit For
Else
End If
Next cell

update cell and paste it to another cell 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.

Excel VBA changing color on cell at the time

Maybe due to my poor English some of you may misunderstood what I wanted to do. Anyways, here is the code that I wanted. After many trial and errors I made it but I doubt its the best way but it works =). If anyone know how to make the code better please tell me =).
I previous post is also below this new code.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim row As Integer
For row = 1 To 1000
If Cells(row, "A").Value = "Yes" Then
Range("B" & row).Interior.ColorIndex = 4
End If
If Cells(row, "A").Value = "yes" Then
Range("B" & row).Interior.ColorIndex = 4
End If
Next row
End Sub
Previous post
I have written a VBA code in excel where I want to change a cell color everytime I write "yes". In this case I write "yes" in column E and column A should change color. I have used a for loop but it does not run as I want. I have a feeling I am thinking a bit wrong...
Private Sub Worksheet_Change(ByVal Target As Range)
Dim cellNr As Long
For cellNr = 1 To 5
If Range("E" & cellNr).Value = "yes" Then
Range("A" & cellNr).Interior.ColorIndex = 5
Else
ActiveCell(0, -2).Interior.ColorIndex = 4
End If
Next cellNr
End Sub
Use conditional formatting with a formula. Starting in row 1 the formula required is
=$E1="yes"
If I understand what you are trying to do with the following line:
ActiveCell(0, -2).Interior.ColorIndex = 4
Replace it with:
ActiveCell.Offset(0, -2).Interior.ColorIndex = 4
As far as I know, the line I told you to remove will always throw an error. It doesn't make any sense syntactically. You should use the Offset function to get positions relative to the Active Cell. But also, this offset function will throw an error if your active cell is in column A or B because there are less than 2 columns to the left of the active cell.
Consider:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rOfInterest As Range
Set rOfInterest = Range("E1:E5")
If Intersect(Target, rOfInterest) Is Nothing Then Exit Sub
v = Target.Value
If v = "yes" Then
Target.Offset(0, -4).Interior.ColorIndex = 5
Else
Target.Offset(0, -4).Interior.ColorIndex = 4
End If
End Sub
and make sure the macro is in the worksheet code area.