Is it possible for worksheet_change to not process the pending/already happened user update by exiting the sub should a condition not be met? I have a logging system and do not want the user to delete more than one cell at a time, since my code handles one-cell-at-a-time updates - Eventually the code should be improved to allow this. But for the meantime:
Private Sub Worksheet_Change(ByVal Target As Excel.Range)
If Selection.Count > 1 Then
MsgBox ("please delete items one at a time")
Exit Sub
End If
This displays the msgbox ok, but proceeds with the deletion of the contents of all cells in the selection, should multiple cells be selected. The deleted contents then gets missed by the logging system.
Ok, trying to get to "final destination" in response to #Gary's Student answer since it isn't really illegal to delete multiple cells.
Partially using #binil 's solution gets me to this pseudocode but I am not good enough to complete it (the cells in the range may be non-contiguous)
Dim theoldvals As Variant
Private Sub Worksheet_Change(ByVal Target As Range)
Dim i As Integer
for each c in the oldvals
Sheets("log").ActiveCell.Value = "OLD value" = oldvals(c)
.ActiveCell.Offset(1, 0) = "NEW value" = Target(c) ' where c is the corresponding cell/index in the target range
Next c
End Sub
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Set theoldvals = Target
End Sub
You can have another sheet (set Visible = xlSheetVeryHidden so users can't just show it through the regular unhiding process) that is a clone of your sheet then. Then any time you are in the Worksheet_Change event you can check your validation conditions. if your conditions fail you grab the old value from your clone sheet, if you pass validation then you write to your regular sheet like normal and duplicate that write to your clone sheet.
You can't prevent something after it has already happened. However:
If Selection.Count > 1 Then
MsgBox ("please delete items one at a time")
ActiveWorkbook.Saved = True
ActiveWorkbook.Close
End If
will prevent the illegal changes from being saved.
Related
How can I automatically execute an Excel macro each time a value in a particular cell changes?
Right now, my working code is:
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("H5")) Is Nothing Then Macro
End Sub
where "H5" is the particular cell being monitored and Macro is the name of the macro.
Is there a better way?
Your code looks pretty good.
Be careful, however, for your call to Range("H5") is a shortcut command to Application.Range("H5"), which is equivalent to Application.ActiveSheet.Range("H5"). This could be fine, if the only changes are user-changes -- which is the most typical -- but it is possible for the worksheet's cell values to change when it is not the active sheet via programmatic changes, e.g. VBA.
With this in mind, I would utilize Target.Worksheet.Range("H5"):
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Target.Worksheet.Range("H5")) Is Nothing Then Macro
End Sub
Or you can use Me.Range("H5"), if the event handler is on the code page for the worksheet in question (it usually is):
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Me.Range("H5")) Is Nothing Then Macro
End Sub
I spent a lot of time researching this and learning how it all works, after really messing up the event triggers. Since there was so much scattered info I decided to share what I have found to work all in one place, step by step as follows:
1) Open VBA Editor, under VBA Project (YourWorkBookName.xlsm) open Microsoft Excel Object and select the Sheet to which the change event will pertain.
2) The default code view is "General." From the drop-down list at the top middle, select "Worksheet."
3) Private Sub Worksheet_SelectionChange is already there as it should be, leave it alone. Copy/Paste Mike Rosenblum's code from above and change the .Range reference to the cell for which you are watching for a change (B3, in my case). Do not place your Macro yet, however (I removed the word "Macro" after "Then"):
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Me.Range("H5")) Is Nothing Then
End Sub
or from the drop-down list at the top left, select "Change" and in the space between Private Sub and End Sub, paste If Not Intersect(Target, Me.Range("H5")) Is Nothing Then
4) On the line after "Then" turn off events so that when you call your macro, it does not trigger events and try to run this Worksheet_Change again in a never ending cycle that crashes Excel and/or otherwise messes everything up:
Application.EnableEvents = False
5) Call your macro
Call YourMacroName
6) Turn events back on so the next change (and any/all other events) trigger:
Application.EnableEvents = True
7) End the If block and the Sub:
End If
End Sub
The entire code:
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Me.Range("B3")) Is Nothing Then
Application.EnableEvents = False
Call UpdateAndViewOnly
Application.EnableEvents = True
End If
End Sub
This takes turning events on/off out of the Modules which creates problems and simply lets the change trigger, turns off events, runs your macro and turns events back on.
Handle the Worksheet_Change event or the Workbook_SheetChange event.
The event handlers take an argument "Target As Range", so you can check if the range that's changing includes the cell you're interested in.
I prefer this way, not using a cell but a range
Dim cell_to_test As Range, cells_changed As Range
Set cells_changed = Target(1, 1)
Set cell_to_test = Range( RANGE_OF_CELLS_TO_DETECT )
If Not Intersect(cells_changed, cell_to_test) Is Nothing Then
Macro
End If
I have a cell which is linked to online stock database and updated frequently. I want to trigger a macro whenever the cell value is updated.
I believe this is similar to cell value change by a program or any external data update but above examples somehow do not work for me. I think the problem is because excel internal events are not triggered, but thats my guess.
I did the following,
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Target.Worksheets("Symbols").Range("$C$3")) Is Nothing Then
'Run Macro
End Sub
I have a table where I want to be able to hide individual rows at a mouse click. The (seemingly) easiest solution I've found is to have a column filled with hyperlinks that call a macro to hide the row that they're in.
There are two ways of calling macros from hyperlinks: using Worksheet_FollowHyperlink with manual hyperlinks, and using =HYPERLINK.
The former works fine, except there's no way (that I've found) to have them generate automatically when new rows are added to the table. I would have to either manually copy them down every time, which is unviable, or add them with VBA, which adds a bunch of complexity to an otherwise simple task.
The latter generates fine, being a formula, but it doesn't actually work. It doesn't trigger Worksheet_FollowHyperlink, and when using =HYPERLINK("#MyFunction()") it just doesn't hide rows (or do much other than editing cells contents).
Function MyFunction()
Set MyFunction = Selection
Selection.EntireRow.Hidden = True
End Function
Is there a good solution to this?
Rather than a Hyperlink, you could handle a Double Click event on the table column
Something like
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
Dim NameOfTableColumn As String
On Error GoTo EH:
NameOfTableColumn = "DblClickToHide" ' update to suit your table
If Not Application.Intersect(Target, Target.ListObject.ListColumns(NameOfTableColumn).DataBodyRange) Is Nothing Then
Target.EntireRow.Hidden = True
Cancel = True
End If
Exit Sub
EH:
End Sub
Please, copy the next code in the sheet code module where the table to be clicked exists. Clicking on each cell in its first column (dynamic to rows adding/insertions/deletions), the clicked row will be hidden:
Option Explicit
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim tbl As ListObject
If Target.cells.CountLarge > 1 Then Exit Sub
Set tbl = Me.ListObjects(1) 'you may use here the table name
If Not Intersect(Target, tbl.DataBodyRange.Columns(1)) Is Nothing Then
Application.EnableEvents = False
Target.EntireRow.Hidden = True
Application.EnableEvents = True
End If
End Sub
It would be good to think about a way to unhide the hidden row if/when necessary. If only a row should be hidden at a time, it is easy to unhide all the rest of column cells...
How can I automatically execute an Excel macro each time a value in a particular cell changes?
Right now, my working code is:
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("H5")) Is Nothing Then Macro
End Sub
where "H5" is the particular cell being monitored and Macro is the name of the macro.
Is there a better way?
Your code looks pretty good.
Be careful, however, for your call to Range("H5") is a shortcut command to Application.Range("H5"), which is equivalent to Application.ActiveSheet.Range("H5"). This could be fine, if the only changes are user-changes -- which is the most typical -- but it is possible for the worksheet's cell values to change when it is not the active sheet via programmatic changes, e.g. VBA.
With this in mind, I would utilize Target.Worksheet.Range("H5"):
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Target.Worksheet.Range("H5")) Is Nothing Then Macro
End Sub
Or you can use Me.Range("H5"), if the event handler is on the code page for the worksheet in question (it usually is):
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Me.Range("H5")) Is Nothing Then Macro
End Sub
I spent a lot of time researching this and learning how it all works, after really messing up the event triggers. Since there was so much scattered info I decided to share what I have found to work all in one place, step by step as follows:
1) Open VBA Editor, under VBA Project (YourWorkBookName.xlsm) open Microsoft Excel Object and select the Sheet to which the change event will pertain.
2) The default code view is "General." From the drop-down list at the top middle, select "Worksheet."
3) Private Sub Worksheet_SelectionChange is already there as it should be, leave it alone. Copy/Paste Mike Rosenblum's code from above and change the .Range reference to the cell for which you are watching for a change (B3, in my case). Do not place your Macro yet, however (I removed the word "Macro" after "Then"):
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Me.Range("H5")) Is Nothing Then
End Sub
or from the drop-down list at the top left, select "Change" and in the space between Private Sub and End Sub, paste If Not Intersect(Target, Me.Range("H5")) Is Nothing Then
4) On the line after "Then" turn off events so that when you call your macro, it does not trigger events and try to run this Worksheet_Change again in a never ending cycle that crashes Excel and/or otherwise messes everything up:
Application.EnableEvents = False
5) Call your macro
Call YourMacroName
6) Turn events back on so the next change (and any/all other events) trigger:
Application.EnableEvents = True
7) End the If block and the Sub:
End If
End Sub
The entire code:
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Me.Range("B3")) Is Nothing Then
Application.EnableEvents = False
Call UpdateAndViewOnly
Application.EnableEvents = True
End If
End Sub
This takes turning events on/off out of the Modules which creates problems and simply lets the change trigger, turns off events, runs your macro and turns events back on.
Handle the Worksheet_Change event or the Workbook_SheetChange event.
The event handlers take an argument "Target As Range", so you can check if the range that's changing includes the cell you're interested in.
I prefer this way, not using a cell but a range
Dim cell_to_test As Range, cells_changed As Range
Set cells_changed = Target(1, 1)
Set cell_to_test = Range( RANGE_OF_CELLS_TO_DETECT )
If Not Intersect(cells_changed, cell_to_test) Is Nothing Then
Macro
End If
I have a cell which is linked to online stock database and updated frequently. I want to trigger a macro whenever the cell value is updated.
I believe this is similar to cell value change by a program or any external data update but above examples somehow do not work for me. I think the problem is because excel internal events are not triggered, but thats my guess.
I did the following,
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Target.Worksheets("Symbols").Range("$C$3")) Is Nothing Then
'Run Macro
End Sub
I have a macro which is basically working as I want (alerting the user when two conflicting checkboxes are selected) - code below. The 1 in G2 is the value generated to indicate this case.
The error message fires on SelectionChange, but this appears to be only when another cell is selected by mouse. The worksheet contains a series of checkboxes for the user to select from, and the intention is for the user to only use the checkboxes, never needing to select or input directly into cells. In which case, the error message would never fire even when the scenario described has occurred.
Is there a way of having a msgbox macro trigger by the update of any checkbox on the sheet?
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Range("G2") = 1 Then
MsgBox "ERROR - Select AND Reject checked"
End If
End Sub
Also, I would like to extend the range to apply to all the cells in column G, I just can't seem to get this to work for me. I have seen a few examples citing "G:G" but I have so far only got this to work for one cell.
Apologies in advance for any glaring errors, I've used Excel for a while now - but I'm brand new to using VBA.
Mutually exclusive options are usually indicated with option buttons (also known as radio buttons) instead of checkboxes. Is there any reason you're not using option buttons for this task?
As far as calling the same code for all checkboxes, the checkboxes would have to be Form Controls (not ActiveX Controls), and you could assign them to this macro:
Sub CheckBox_Clicked()
Dim chk As CheckBox
Set chk = ActiveSheet.CheckBoxes(Application.Caller)
MsgBox chk.Name
End Sub
And lastly, for your SelectionChange event to monitor an entire column, it would look similar to this:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim ClickedCell As Range
Dim rngClicked As Range
Application.EnableEvents = False
Set rngClicked = Intersect(Columns("G"), Target)
If Not rngClicked Is Nothing Then
For Each ClickedCell In rngClicked.Cells
If ClickedCell.Value = 1 Then MsgBox "ERROR - Select AND Reject checked"
Next ClickedCell
End If
Application.EnableEvents = True
End Sub
Is there a simple way to get Excel to automatically execute a macro whenever a cell is changed?
The cell in question would be in Worksheet("BigBoard").Range("D2")
What I thought would be a simple Google inquiry is proving to be more complicated - every sample involved intersects (whatever those are) or color formatting or any other number of things that appear to be irrelevant.
Yes, this is possible by using worksheet events:
In the Visual Basic Editor open the worksheet you're interested in (i.e. "BigBoard") by double clicking on the name of the worksheet in the tree at the top left. Place the following code in the module:
Private Sub Worksheet_Change(ByVal Target As Range)
If Intersect(Target, Me.Range("D2")) Is Nothing Then Exit Sub
Application.EnableEvents = False 'to prevent endless loop
On Error Goto Finalize 'to re-enable the events
MsgBox "You changed THE CELL!"
Finalize:
Application.EnableEvents = True
End Sub
Another option is
Private Sub Worksheet_Change(ByVal Target As Range)
IF Target.Address = "$D$2" Then
MsgBox("Cell D2 Has Changed.")
End If
End Sub
I believe this uses fewer resources than Intersect, which will be helpful if your worksheet changes a lot.
In an attempt to find a way to make the target cell for the intersect method a name table array, I stumbled across a simple way to run something when ANY cell or set of cells on a particular sheet changes. This code is placed in the worksheet module as well:
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Cells.Count > 0 Then
'mycode here
end if
end sub
In an attempt to spot a change somewhere in a particular column (here in "W", i.e. "23"), I modified Peter Alberts' answer to:
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Target.Column = 23 Then Exit Sub
Application.EnableEvents = False 'to prevent endless loop
On Error GoTo Finalize 'to re-enable the events
MsgBox "You changed a cell in column W, row " & Target.Row
MsgBox "You changed it to: " & Target.Value
Finalize:
Application.EnableEvents = True
End Sub
I was creating a form in which the user enters an email address used by another macro to email a specific cell group to the address entered. I patched together this simple code from several sites and my limited knowledge of VBA. This simply watches for one cell (In my case K22) to be updated and then kills any hyperlink in that cell.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim KeyCells As Range
' The variable KeyCells contains the cells that will
' cause an alert when they are changed.
Set KeyCells = Range("K22")
If Not Application.Intersect(KeyCells, Range(Target.Address)) _
Is Nothing Then
Range("K22").Select
Selection.Hyperlinks.Delete
End If
End Sub