I am new to vba and I am running into a Type mismatch error when I delete multiple cells in my workbook, and it is failing at the If statement here:
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Value = "Multiple Choice - Multiple Answer" Then
MsgBox "You selected MC-MA"
End If
End Sub
I am wondering if there's a way to check if a user is deleting contents of a cell and NOT run this if statement if that is the case?
I have tried If Trim(Target.Value) <> Empty but it throws the error I'm guessing because Im trying to run an If statement on a value that doesn't exist.
I expect If Trim(Target.Value) <> Empty to make it skip the above code but it throws the Type mismatch error.
Try the SelectionChange Event like this:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim cel As Range
For Each cel In Target
If cel.Value = "Multiple Choice - Multiple Answer" Then
MsgBox "You selected MC-MA"
End If
Next
End Sub
In Change event even if you try and check cells one by one, you won't spot the cell with that match because the code runs after the delete has been executed. So by the time it comes to the If statement, the cell is already blank.
I figured out a way around it. If I just do an If statement that checks if Target.Count = 1 then run the code it works!
Related
I am having a problem with Excel crashing, when I run VBA code on an excel sheet.
I'm trying to add the following formula on worksheet change:
Private Sub Worksheet_Change(ByVal Target As Range)
Worksheets("testpage").Range("A1:A8").Formula = "=B1+C1"
End Sub
When this code is run i get a message saying "excel has encountered a problem and needs to close" and excel closes.
If I run the code in the Worksheet_Activate() procedure, it works fine and doesn't crash
Private Sub Worksheet_Activate()
Worksheets("testpage").Range("A1:A8").Formula = "=B1+C1"
End Sub
But I really need it to work in the Worksheet_Change() procedure.
Has anyone experienced similar crashes when using the Worksheet_Change() event and can anyone point in the right direction to fix this issue ?
I recommend this when using Worksheet_Change
You do not need the sheet name. In a Sheet Code Module, an unqualified Range reference refers to that sheet. That said, it is clearer to use the Me qualifier. If you are trying to use another sheet, then qualify the range reference with that sheet.
Whenever you are working with Worksheet_Change event, always switch Off events if you are writing data to any cell. This is required so that the code doesn't retrigger the Change event, and go into a possible endless loop
Whenever you are switching off events, use error handling to turn it back on, else if you get an error, the code will not run the next time.
Try this
Private Sub Worksheet_Change(ByVal Target As Range)
On Error GoTo Whoa
Application.EnableEvents = False
Me.Range("A1:A8").Formula = "=B1+C1"
Letscontinue:
Application.EnableEvents = True
Exit Sub
Whoa:
MsgBox Err.Description
Resume Letscontinue
End Sub
Few other things that you may want to know when working with this event.
If you want to ensure that the code doesn't run when more than one cell is changed then add a small check
Private Sub Worksheet_Change(ByVal Target As Range)
'~~> For Excel 2003
If Target.Cells.Count > 1 Then Exit Sub
'
'~~> Rest of code
'
End Sub
The CountLarge was introduced in Excel 2007 onward because Target.Cells.Count returns an Long value which can error out in Excel 2007 becuase of increased total cells count.
Private Sub Worksheet_Change(ByVal Target As Range)
'~~> For Excel 2007
If Target.Cells.CountLarge > 1 Then Exit Sub
'
'~~> Rest of code
'
End Sub
To work with all the cells that were changed use this code
Private Sub Worksheet_Change(ByVal Target As Range)
Dim aCell As Range
For Each aCell In Target.Cells
With aCell
'~~> Do Something
End With
Next
End Sub
To detect change in a particular cell, use Intersect. For example, if a change happens in Cell A1, then the below code will fire
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Me.Range("A1")) Is Nothing Then
MsgBox "Cell A1 was changed"
'~~> Your code here
End If
End Sub
To detect change in a particular set of range, use Intersect again. For example, if a change happens in range A1:A10, then the below code will fire
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Me.Range("A1:A10")) Is Nothing Then
MsgBox "one or more Cells in A1:A10 range was changed"
'~~> Your code here
End If
End Sub
Note: If you were getting an error earlier and you made the above changes and If your code is still not working then it is possible that the events have not been reset. In the Immediate Window, type Application.EnableEvents = True and press the ENTER key. This will reset it to True. If you do not see the Immediate Window, the press the shortcut key Ctl+G to launch the Immediate Window.
Excel was crashing, not the VBA function.
The events were not disabled and the call stack was filled by an infinite loop of OnChange events.
A little advice that helps finding this type of errors: set a breakpoint on the first line of the event, then execute it step by step pressing F8.
Also this solution is good:
Option Explicit
Private Busy As Boolean
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Busy Then
Busy = True
Range("A1:A8").Formula = "=B1+C1"
Busy = False
End If
End Sub
I have the following worksheet function saved in the Sheet1 (DataInput) module of VBA
Sub worksheet_change(ByVal target As Range)
Set target = Range("F67")
If target.Value = "YES" Then
Call ThisWorkbook.Red_Macro_Text
End If
End Sub
This code calls the following code in ThisWorkbook
Public Sub Red_Macro_Text()
ThisWorkbook.Worksheets("DataInput").Range("B68").Value2 = ThisWorkbook.Worksheets("Text").Range("B3").Value2
End Sub
The purpose of the code is as follows:
The code uses an event trigger to watch for changes to worksheet ‘datainput’ cell F67.
Whenever the user selects ‘YES’ from a drop down menu in cell F67 it calls the subroutine in ThisWorkbook.
The code uses the range function to assign the value of 'text' worksheet B3 into 'datainput' worksheet B68 cell.
The trigger event does call the code and the content of B3 does get transferred to B68 however following this I then get the error message:
Run-time error ‘-2147417848 (80010108)’: Automation error The object
invoked has disconnected from its clients.
Please note I do not want to use an IF statement in excel to achieve this as I am trying to automate the process in VBA.
I have been searching for a solution to this problem for some time without success.
Does anyone know how to solve this problem as I have reached the limit of my understanding!
All help is appreciated
It doesn't make sense to Set target = Range("F67") because it is already retrieved as parameter ByVal target As Range
If you want to run the code only if F67 changes use the following instead:
Sub worksheet_change(ByVal Target As Range)
If Not Intersect(Target, Target.Parent.Range("F67")) Is Nothing Then
If Target.Parent.Range("F67").Value = "YES" Then
ThisWorkbook.Red_Macro_Text
End If
End If
End Sub
Note that Call is not needed to call a macro.
Try
Sub worksheet_change(ByVal Target As Range)
If Target.Address = Range("F67").Address Then
If Target.Value = "YES" Then
Call ThisWorkbook.Red_Macro_Text
End If
End If
End Sub
I am wanting to display a message box each time the cell value in my column is changed.
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Cells.Count < 3 And Target.Column = 13 Then
If Not Intersect(Target, Target.Worksheet.Range("M:M")) Is Nothing Then
MsgBox "hEY"
End If
End If
End Sub
Ideally i'd like to try and avoid worksheet change event since this seems to slow down my spreadhseet for everything else, including when users type in other cells. The cursor shows a loading cursor as if vba is trying to work in the background.
Is there a way around this?
You could simplify your code, just check if the cells being changed are in Column M, and then check that the number of cells is less than 3.
If those 2 Ifs are not met, the code does nothing, just goes directly to End Sub.
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("M:M")) Is Nothing Then
If Target.Cells.Count < 3 Then MsgBox "Hey"
End If
End Sub
In this case, you can't display a MessageBox without putting code in the event. The best you can do is to test the conditions that will likely fail first, so the code test fewer times conditions inside de event and run faster.
Private Sub Worksheet_Change(ByVal Target As Range)
If Intersect(Target, Me.Range("M:M")) Is Nothing Then GoTo Quit
If Target.Cells.Count < 3 Then GoTo Quit
MsgBox "Hey!"
Quit:
End Sub
In my experience, you should avoid this kind of code because of the reasons you stated. I always set my worksheet free and create a 'Validate' button that points out the user where errors were found (tough I don't know what you want to accomplish with your code).
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.
I have a problem with this code:
Private Sub Worksheet_Change(ByVal Target As Range)
If Target Is Nothing Then Exit Sub
MsgBox Target.Value
End Sub
After I enter text to some cell I get a message box, but if I try to delete the row where I just entered a text, I get an error:
Run-time error '13': Type mismatch
How can I fix this error? Why the condition doesn't catch it?
When you delete row as a result the whole row is target object in your procedure. Therefore your macro is not able to return value of the row.
What programmers usually do is additional condition which is checked before your message box:
If Target.Count =1 Then
'your messagebox here
End If