I have a cell A1 which extracts values from a server every n seconds, however using the macro below (which is currently used) is not suitable:
Dim preVal As String
Dim count As Integer
'Intention is if cell A1 changes, record changes to Column C and Column D
Private Sub Worksheet_Change(ByVal Target As Range)
If Target = Range("A1") Then
Call cellchange(Range("A1"))
End If
End Sub
Private Sub cellchange(ByVal a As Range)
'If row is empty, filled into that row, if not skip to next one
If a.Value <> preVal Then
count = count + 1
'copy the value of A1 from sheet 1
preVal = Sheets("Sheet1").Range("A1").Value
Cells(count, 4).Value = a.Value
'copy the values of time of which data change detected
Cells(count, 3) = Now()
End If
End Sub
In a simplest way, the cell A1 will be updated every few seconds from a server, so I need the macro to be updated/trigger when it detects changes in cell A1 that are not from human input.
You need to use something that really checks if your target cells is updated. usually application.intersect are used. Here I am using address property.
Dim preVal As String
Dim count As Integer
'Intention is if cell A1 changes, record changes to Column C and Column D
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.address = Range("A1").address Then
cellchange target
End If
End Sub
Private Sub cellchange(ByVal a As Range)
'If row is empty, filled into that row, if not skip to next one
If a.Value <> preVal Then
count = count + 1
'copy the value of A1 from sheet 1
preVal = Sheets("Sheet1").Range("A1").Value
Cells(count, 4).Value = a.Value
'copy the values of time of which data change detected
Cells(count, 3) = Now
End If
End Sub
Hope it helps.
Regards,
M
Related
I have an excel file, all the cells of the column A contain a value (numerical value), then there is the column B, which contains other values.
I would like to write a script in vba (or also other options, if there are alternatives) to control the change the values in column A.
To be clear, the excel user can change the cell A1 only if the cell B1 has a value equal to zero.
The same for the following rows, so the row A2 can be changed only if B2 is equal to zero, and so on.
I have searched on internet but I can't find a good solution, in particular because this must work on a large number of cells.
Thanks
Please, copy the next event code in the respective sheet code module, and play with changing:
Option Explicit
Private newVal
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.cells.count > 1 Then Exit Sub
If Target.column = 1 Then
If Target.Offset(, 1).Value <> 0 Then
Application.EnableEvents = False 'to avoind triggering again the Change event
newVal = Target.Value
Application.Undo
Application.EnableEvents = True
End If
End If
End Sub
You can use Data validation for this:
Select the range (ex: A1:A10)
Go to Data -> Data tools -> Data Validation
Choose the following options:
Allow "Custom"
Formula: =B1:B10=0
A Worksheet Selection Change: Restrict Column Access
With the current setup (A2, B), the following will allow access to column A (more accurately, to A2:A1048576) only if a single cell is to be selected and the cell in the same row of column B is equal to 0.
Optionally, if you choose the out-commented If statement, it will also allow access if the cell in column B is empty.
An empty cell is also numeric and equal to 0. It isn't of type Double though, but of type Empty.
Sheet Module e.g. Sheet1 (Not Into ThisWorkbook or Standard Module e.g. Module1)
Option Explicit
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Const TGT_FIRST_CELL As String = "A2"
Const SRC_COLUMN As String = "B"
Dim trg As Range
With Me.Range(TGT_FIRST_CELL)
Set trg = .Resize(Me.Rows.Count - .Row + 1)
End With
Dim irg As Range: Set irg = Intersect(trg, Target)
If irg Is Nothing Then Exit Sub
Dim ColumnOffset As Long
ColumnOffset = Me.Columns(SRC_COLUMN).Column - irg.Column
Dim srg As Range: Set srg = irg.Offset(, ColumnOffset)
If srg.Cells.Count = 1 Then
Dim sValue As Variant: sValue = srg.Value
If VarType(sValue) = vbDouble Then ' write just if 0
' or:
'If IsNumeric(sValue) Then ' write if 0 or empty
If sValue = 0 Then Exit Sub
End If
End If
srg.Cells(1).Select
End Sub
How can I force user to enter negative number in Excel?
Basically column A can only be "W" or "X". Whenever column A has "W", i want column B to reflect a negative number, even if the user has keyed in a positive number.
"W" in column A corresponds to a negative value in column B
"X" in column B corresponds to a positive value in column B.
Thanks for the help!
No VBA needed. Just use data validation with the following formula
=OR(AND(A1="W",B1<0),AND(A1="X",B1>0))
Image 1: Using data validation W in column A only allows negatives in column B, X in column A only allows positives in column B.
Install the code below in the code module of the worksheet on which you want to control the input. It's a module that already exists in your VB Project. Any module you have to create is the wrong one and won't work. Look for a module with a double name like Sheet1 (Sheet1).
Private Sub Worksheet_Change(ByVal Target As Range)
' 058
Dim Rng As Range
Dim Numb As Variant
Dim NewNumb As Double
' ignore changes to more than one cell (such as pasting)
If Target.CountLarge > 1 Then Exit Sub
' this range starts in A2 and covers all used cells in columns A:B
Set Rng = Range(Cells(2, "A"), Cells(Rows.Count, "A").End(xlUp)) _
.Resize(, 2)
' skip if the changed cell is not within the defiend range
If Not Application.Intersect(Target, Rng) Is Nothing Then
' take no action of the value in column A isn't "X" or "W"
With Target
Numb = Cells(.Row, "B").Value
' take no action if the cell in column B has no value
If Numb Then
If Cells(.Row, "A").Value = "W" Then
NewNumb = Abs(Val(Numb)) * -1
ElseIf Cells(.Row, "A").Value = "X" Then
NewNumb = Abs(Val(Numb))
End If
' prevent changes made from calling this procedure
Application.EnableEvents = False
' don't take action if the value in column A
' was neither X nor W
If Numb And (Numb <> NewNumb) Then _
Cells(.Row, "B").Value = NewNumb
Application.EnableEvents = True
End If
End With
End If
End Sub
The code works on columns A and B. To modify these targets isn't difficult. For now, when a cell in either column is changed the procedure may take action. For the rules by which it will not take action please read the comments in the code. When it does take action it will make sure that any entry in column B is negative if the letter in column A is W and positive when it's X, regardless of what sign the user entered.
A little VBA in your worksheet module will take care of that:
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Const SourceColumn As Long = 1
Const TargetColumn As Long = 2
Const NegatorSymbol As String = "W"
Dim SourceRange As Excel.Range
Dim TargetRange As Excel.Range
Dim Sign As Long
Dim TargetValue As Long
If Target.Column = TargetColumn Then
Set SourceRange = Cells(Target.Row, SourceColumn)
If UCase(SourceRange.Value) = NegatorSymbol Then
Sign = -1
Else
Sign = 1
End If
TargetValue = Sign * Abs(Target.Value)
If Target.Value <> TargetValue Then
Target.Value = TargetValue
End If
ElseIf Target.Column = SourceColumn Then
Set TargetRange = Cells(Target.Row, TargetColumn)
If UCase(Target.Value) = NegatorSymbol Then
Sign = -1
Else
Sign = 1
End If
TargetValue = Sign * Abs(TargetRange.Value)
If TargetRange.Value <> TargetValue Then
TargetRange.Value = TargetValue
End If
End If
End Sub
You can set on column B a data validation Custom with this formula:
=OR(AND(A1="W";B1<0);AND(A1<>"W";B1>0))
[EDIT]
I was late to the party...
I have a large workbook and am trying to increase performance.
Is it possible/viable to store my formulas in some sort of list contained within the code rather than in the cells on the spreadsheet?
Variable SelectedRow = the currently selected row
For example:
ColumnBFormula = A(SelectedRow) + 1
ColumnCFormula = A(SelectedRow) + 2
If the user enters 4 in cell A3, then the macro writes formulas above ONLY in empty cells B3 and C3, then converts to values. The rest of the spreadsheet remains unchanged (should only have values everywhere).
Then the user enters a 6 in cell A4 and the spreadsheet writes the formulas to empty cells B4 and C4, calculates then converts to values.
Thanks
Try:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim Lastrow As Long
'Refer to Sheet1
With ThisWorkbook.Worksheets("Sheet1")
'Check if Column A affected
If Not Intersect(Target, Range("A:A")) Is Nothing And IsNumeric(Target) Then
'Disable event to avoid event trigger
Application.EnableEvents = False
Target.Offset(0, 1).Value = Target + 1
Target.Offset(0, 2).Value = Target + 2
'Enable event
Application.EnableEvents = True
End If
End With
End Sub
Instructions:
Enable Events:
Given you know what you want the code to do, you could do this without entering formulas.
In the VBA editor, add this code into the "ThisWorkbook" object ...
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
Dim objCell As Range
Application.EnableEvents = False
For Each objCell In Target.Cells
If objCell.Column = 1 Then
If objCell.Value = "" Then
objCell.Offset(0, 1) = ""
objCell.Offset(0, 2) = ""
Else
objCell.Offset(0, 1) = objCell.Value + 1
objCell.Offset(0, 2) = objCell.Value + 2
End If
End If
Next
Application.EnableEvents = True
End Sub
Hopefully that works for you.
FYI - You'll need to add the relevant error checking for values if not numeric etc, it will need to be improved.
I am new to VBA and was wondering how I combine 2 worksheet_change scripts, or if there is something else I should use.
I have a dropdown list which when selected give dependancy to another dropdown list.
For the first dropdown I have code which filters the columns so the other columns are hidden. There are several columns which have the same text in row 3 making multiple columns associated with the first dropdown. The code below works fine for B2.
Users may stop at the first dropdown, but if they then select the second dropdown I need the spreadsheet to filter the columns further so only one column is displayed. The heading titles are in row 4.
At the moment I have:
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Address = "$B$2" Then
Dim the_selection As String
Dim the_group As String
the_selection = Sheet1.Range("B2")
Dim Rep as Integer
For Rep = 5 to 100
the_column = GetColumnLetter_ByInteger(Rep)
the_group = Sheet1.Range(the_column & "3")
If the_selection = the_group Then
Sheet1.Range(the_column & ":" & the_column).EntireColumn.Hidden = False
Else
Sheet1.Range(the_column & ":" & the_column).EntireColumn.Hidden = True
End If
Next Rep
End If
End Sub
If I try and create a Worksheet_SelectionChange for the C2 dropdown it sort of works but I have to click out of the cell and then in again for it to filter properly. This is not ideal. Is there a way of incorporating the codes together in the Worksheet_change.
Additionally, is it possible for the second selection to also filter the rows so only those with values appear and the blank ones are hidden? The second filter would always filter to one column and never more than one. What code would I add to reset the row filter when a user selected another dropdown?
Any help is appreciated.
Lando :)
Your original code could be rewritten as
Private Sub Worksheet_Change(ByVal Target As Range)
Dim the_selection As String
Dim the_group As String
Dim Rep As Long
If Target.Address = "$B$2" Then
the_selection = Sheet1.Range("B2") 'If this code is in Sheet1 you can just use "the_selection=Target".
For Rep = 5 To 100
the_group = Sheet1.Cells(3, Rep)
Sheet1.Columns(Rep).Hidden = (the_selection <> the_group)
Next Rep
End If
End Sub
Sheet1.Columns(Rep).Hidden requires TRUE or FALSE to hide/show the
column.
(the_selection <> the_group) will return TRUE if
the_selection is different from the_group and FALSE if not.
Your combined code could be:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim the_selection As String
Dim the_group As String
Dim Rep As Long
If Not Intersect(Target, Range("B2:C2")) Is Nothing Then
the_selection = Target
'Unhide all columns if B2 is changed.
If Target.Address = "$B$2" Then
Sheet1.Columns.Hidden = False
End If
For Rep = 5 To 100
the_group = Sheet1.Cells(Target.Column + 1, Rep)
Select Case Target.Address
Case "$B$2"
Sheet1.Columns(Rep).Hidden = (the_selection <> the_group)
Case "$C$2"
If Not Sheet1.Columns(Rep).Hidden Then
Sheet1.Columns(Rep).Hidden = (the_selection <> the_group)
End If
End Select
Next Rep
End If
End Sub
The code will take the value from B2 or C2 (the_selection=Target).
B2 looks at row 3, C2 looks at row 4 - column B is also column 2, column C is also column 3 so the code just adds one to get the correct row number (the_group = Sheet1.Cells(Target.Column + 1, Rep)).
If the value being changed is C2 then you don't want to unhide any columns already hidden by B2 so the code checks if the column is not already hidden before attempting to hide it (If Not Sheet1.Columns(Rep).Hidden Then)
I am looking to parse an entire row in a particular excel sheet for any change in data in that row. If there is any change in data in that row then i want to add the date in which that particular cell of that row. I want to pass the row as an input. I tried the following code but it doesnt work.
Private Function User_func1(ByVal i As Long)
Dim j As Long
For j = 1 To j = 100
If Cells(i, j).Value > 1 Then
Cells(i, 2) = Now()
End If
Next j
End Function
You can use the Worksheet_Change event in the sheet you want to scan.
Option Explicit
Const RowtoTest = 2
Private Sub Worksheet_Change(ByVal Target As Range)
Application.EnableEvents = False
If Target.Row = RowtoTest Then
Target.Value = Date
End If
Application.EnableEvents = True
End Sub
Option 2: Get the row to test from a certain cell, lets say Cell "A1" (value is set to 2, means look for changes in cells in row 2).
Private Sub Worksheet_Change(ByVal Target As Range)
Application.EnableEvents = False
' compare the row number to the number inputted as the row to test in cell A1
If Target.Row = Range("A1").Value Then
Target.Value = Date
End If
Application.EnableEvents = True
End Sub