MsgBox appears multiple times after data is pulled from a sheet - vba

I'd like to get some help with the following code. I am very new to this but I think it's an easy solution I'm just unable to retrofit suggestions from other searches into my code.
The msgboxes are working fine on the first pass to check if the text box values are correct but when I am checking to see if a formula result from a sheet is correct I'm getting 5 message boxes popping up.
Hope this makes sense, let me know if you have any suggestions!
`Private Sub SpeedCommand_Click()
Dim ctl As Control
If TextBox1AM180.Value > 12000 And TextBox1AM180.Value <> "" Then
MsgBox "Rate Value is out of range for this boom. Ensure rate value is less than 12,000 lbs./acre", vbExclamation, "Main Bin Application Rate"
Me.TextBox1AM180.SetFocus
Exit Sub
End If
If (TextBox2AM180.Value > 120 Or TextBox2AM180.Value < 20) And TextBox2AM180.Value <> "" Then
MsgBox "Density Value is out of range. Ensure density value is between 20 and 120 lbs./cu ft.", vbExclamation, "Main Bin Density"
Me.TextBox2AM180.SetFocus
Exit Sub
End If
If TextBox3AM180.Value > 12000 And TextBox3AM180.Value <> "" Then
MsgBox "Rate Value is out of range for this boom. Ensure rate value is less than 12,000 lbs./acre", vbExclamation, "Granular Bin Application Rate"
Me.TextBox3AM180.SetFocus
Exit Sub
End If
If (TextBox4AM180.Value > 120 Or TextBox4AM180.Value < 20) And TextBox4AM180.Value <> "" Then
MsgBox "Density Value is out of range. Ensure density value is between 20 and 120 lbs./cu ft.", vbExclamation, "Granular Bin Density"
Me.TextBox4AM180.SetFocus
Exit Sub
End If
' Write data to worksheet
With Range("B4")
.Offset(0, 0).Value = Me.TextBox1AM180.Value
.Offset(1, 0).Value = Me.TextBox2AM180.Value
.Offset(5, 0).Value = Me.TextBox3AM180.Value
.Offset(6, 0).Value = Me.TextBox4AM180.Value
End With
If Range("MaxSpeed1").Value > 30 Then
MsgBox "Based upon rate and density, speed is restricted by machine top end application speed."
Exit Sub
End If
If Range("MaxSpeed2").Value > 30 Then
MsgBox "Based upon rate and density, speed is restricted by machine top end application speed."
Exit Sub
End If
' Hide the form
frmAirmax.Hide

Use the Application.EnableEvents property to temporarily disable events from firing and then re-enable them when you're done.
Something like this:
Application.EnableEvents = False
With Range("B4")
.Offset(0, 0).Value = Me.TextBox1AM180.Value
.Offset(1, 0).Value = Me.TextBox2AM180.Value
.Offset(5, 0).Value = Me.TextBox3AM180.Value
.Offset(6, 0).Value = Me.TextBox4AM180.Value
End With
Application.EnableEvents = True

Related

Excel VBA code executes when deleting/copying/pasting more than one cell at a time

These blocks of code seems to execute when you delete or edit more than one cell in the target columns at a time. In most cases the user is trying to paste allowed values into these cell, just more than one cell at a time. This causes users to have to deal with message boxes that pop up for no reason. Any ideas on how to have the same error checking, but without the message box popping up when you select or alter more than one cell? This code is pasted in a microsoft excel object (sheet code).
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Column = 14 Then 'COLUMN N
If Not IsNumeric(Target.Value) Then
MsgBox "Please enter LBS ran with only numbers.", vbExclamation
Exit Sub
End If
If Len(Target.Value) < 4 And Target.Value <> 0 Then
MsgBox "Please enter LBS ran with comma included.", vbExclamation
Exit Sub
End If
End If
If Target.Column = 15 Then 'COLUMN O
If Not IsNumeric(Target.Value) Then
MsgBox "Please enter LBS ran with only numbers.", vbExclamation
Exit Sub
End If
If Len(Target.Value) < 4 And Target.Value <> 0 Then
MsgBox "Please enter LBS ran with comma included.", vbExclamation
Exit Sub
End If
End If
'IF COLUMN Q IS MARKED, BUT NOT COMPLETE TEXT TURNS RED
If Target.Column = 18 Then
If Target.Value = "YES" Or Target.Value = "Yes" Or Target.Value = "yes" Then 'Marks text green
With Target.Font
.Color = -16724992
.TintAndShade = 0
End With
Else
With Target.Font
.Color = vbRed
.TintAndShade = 0
End With
End If
End If
End Sub
You can always just check how many Targets were selected, and use that in an If statement:
If target.Count = 1 Then msgbox("My Message!")
Since it's a simple one liner If statement, you can do it all in one line instead of, say,
If target.Count = 1 Then
msgbox("My Message!")
End If
And, looking more over the code, perhaps wrap the whole first part in such a statement, since all you're doing is putting up a message:
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Count = 1 Then ' <-------- Checking how many cells were changed.
If Target.Column = 14 Then 'COLUMN N
If Not IsNumeric(Target.Value) Then
MsgBox "Please enter LBS ran with only numbers.", vbExclamation
Exit Sub
End If
If Len(Target.Value) < 4 And Target.Value <> 0 Then
MsgBox "Please enter LBS ran with comma included.", vbExclamation
Exit Sub
End If
End If
If Target.Column = 15 Then 'COLUMN O
If Not IsNumeric(Target.Value) Then
MsgBox "Please enter LBS ran with only numbers.", vbExclamation
Exit Sub
End If
If Len(Target.Value) < 4 And Target.Value <> 0 Then
MsgBox "Please enter LBS ran with comma included.", vbExclamation
Exit Sub
End If
End If
End If 'Target.Count = 1
'IF COLUMN Q IS MARKED, BUT NOT COMPLETE TEXT TURNS RED
' << Rest of your code here >>

VBA Message box to work through a filtered selection of data

I'm trying to create a macro that I can run on a worksheet that uses autofilters. In an ideal world my macro should display a message box that gives me three options:
1) Run a specific set of VBA instructions (in the code below it is to colour cell B2) on the first visible row (ignoring the header) then move to the next visible row and display the message box again.
2) Skip this row, find the next visible row and display the message box again.
3) Quit the macro.
I have the bare bones of the macro below however I feel I'm missing some clever way of displaying the message box again after the first two buttons are pressed. Also I'm not convinced by my code to end the macro.
FYI: The reason for the message box rather than a flat out looped macro is that the filters regularly change and I'm looking to reduce the need to rewrite the code based on the necessary filters.
Sub Msg_exe()
Dim Option_Menu As Integer
Dim strMsg As String
Dim strTitle As String
Range("B2").Select
strMsg = "Continue with this row"
strTitle = "Alert"
Option_Menu = MsgBox(strMsg, vbYesNoCancel + vbQuestion, strTitle)
Select Case Option_Menu
Case 6 'code to colour the cell goes here
Selection.Font.ColorIndex = 25
Selection.Interior.ColorIndex = 33
ActiveCell.Offset(1, 0).Activate
Do While ActiveCell.EntireRow.Hidden = True
ActiveCell.Offset(1, 0).Activate
Loop
'I need some code to show the message box again ready for the next row
Case 7 'code to skip to the next visable line goes here
ActiveCell.Offset(1, 0).Activate
Do While ActiveCell.EntireRow.Hidden = True
ActiveCell.Offset(1, 0).Activate
Loop
'I need some code to show the message box again ready for the next row
Case 2 'the code to end the macro goes here (I hope this is correct)
End
End Select
End Sub
You could loop through the rows, beginning at whichever row you decide, and ascertain if the calculation is required thereafter (I haven't tested so please check this matches your requirements first):
Sub Msg_exe()
Dim cur_row as long
Dim Option_Menu As Integer
Dim strMsg As String
Dim strTitle As String
For cur_row = 2 to Range("A65000").End(xlUp).Row 'Modify this row referenced to suit
if not Range("B" & cur_row).EntireRow.Hidden then
Range("B" & cur_row).Select
strMsg = "Continue with this row"
strTitle = "Alert"
Option_Menu = MsgBox(strMsg, vbYesNoCancel + vbQuestion, strTitle)
Select Case Option_Menu
Case 6 'code to colour the cell goes here
Selection.Font.ColorIndex = 25
Selection.Interior.ColorIndex = 33
Case 7 'code to skip to the next visible line goes here
Case 2 'the code to end the macro goes here (I hope this is correct)
Exit Sub
End Select
End If
Next
End Sub

Checkboxes are running macro on selected cell row; Need them to run on linked cell row

I have a workbook in which specific line items are to be completed by a staff member and, once completed, they are to be checked off as complete. This triggers the row/range to the left of the checkbox to be selected, copied and pasted into the next worksheet on the first available row. The current row is then cleared from the first worksheet. Each worksheet has the checkboxes pre-filled in and pre-linked to cells. The issue I'm having is that when the checkbox is selected, the runall macro activates on the row that is currently selected instead of the row that the checkbox resides in and is linked to the cell in. So, for example, if the checkbox is in row M2 but the currently selected cell is B8, the macro will try to copy and paste row 8 instead of the intended row 2. As there is no undo with macros this results in a major headache. Any help would be greatly appreciated!
Sub RUNALLOPEN()
Dim response As VbMsgBoxResult
response = MsgBox("Are you sure you wish to clear this row and send to the Lab?", vbYesNo + vbExclamation, "Confirm Error Resolution")
If response = vbNo Then
Dim cbx As CheckBox
Set cbx = ActiveSheet.CheckBoxes(Application.Caller)
With cbx.TopLeftCell.Offset(0, -1)
cbx.Value = xlOff
End With
Exit Sub
End If
If response = vbYes Then
'rest of code
Call movedataOPEN2LAB
Call clearcellsOPEN
End If
End Sub
Sub movedataOPEN2LAB()
Dim cbx As CheckBox
'Application.Caller returns the name of the CheckBox that called this macro
Set cbx = ActiveSheet.CheckBoxes(Application.Caller)
'.TopLeftCell returns the cell address located at the top left corner of the cbx checkbox
With cbx.TopLeftCell.Offset(0, -1)
'Check the checkbox status (checked or unchecked)
If cbx.Value = xlOn Then
' Checkbox is Checked
Range(Cells(cbx.TopLeftCell.Offset(0, -1).Row, 1), Cells(cbx.TopLeftCell.Offset(0, -1).Row, 11)).Select
Selection.Copy
Sheets("Lab").Select
Range("A" & Rows.Count).End(xlUp).Offset(1).Select
ActiveSheet.Paste
ActiveSheet.Range("H" & Selection.Row).Formula = "=VLOOKUP(INDIRECT(""G"" & ROW()),'Source Data'!$D$1:$J$36,6,FALSE)"
ActiveSheet.Range("I" & Selection.Row).Value = "Lab"
Range("A2").Select
End If
End With
End Sub
Sub clearcellsOPEN()
On Error Resume Next
Worksheets("Open").Activate
Range(Cells(Selection.Row, 1), Cells(Selection.Row, 15)).Select
Selection.SpecialCells(xlCellTypeConstants).ClearContents
Range(Cells(Selection.Row, 1), Cells(Selection.Row, 1)).Select
End Sub
Thank you for your help! Here's what I came up with:
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Column = 13 Then
'If UCase(Target.Value) <> "X" Then
' Dim response As VbMsgBoxResult
' response = MsgBox("You must input 'x' in order to move this row.", vbOKOnly + vbExclamation, "ERROR")
' Exit Sub
' End If
If UCase(Target.Value) = "X" Then
response = MsgBox("Are you sure you wish to clear this row and send to the Lab?", vbYesNo + vbExclamation, "Confirm Error Resolution")
If response = vbNo Then
Target.Value = ""
Exit Sub
End If
If response = vbYes Then
'rest of code
Target.Cells.Offset(0, -12).Select
Range(Cells(ActiveCell.Row, 1), Cells(ActiveCell.Row, 11)).Select
Selection.Copy
With Sheets("Lab")
.Select
.Range("A" & Rows.Count).End(xlUp).Offset(1, 0).Select
End With
ActiveSheet.Paste
ActiveSheet.Range("H" & Selection.Row).Formula = "=VLOOKUP(INDIRECT(""G"" & ROW()),'Source Data'!$D$1:$J$36,6,FALSE)"
ActiveSheet.Range("I" & Selection.Row).Value = "Lab"
With Sheets("Open")
.Select
On Error Resume Next
Target.Cells.Offset(0, -12).Select
Range(Cells(ActiveCell.Row, 1), Cells(ActiveCell.Row, 14)).Select
Selection.SpecialCells(xlCellTypeConstants).ClearContents
End With
End If
End If
End If
End Sub
There are many other ways to accomplish that than checkboxes... A "cleaner" one that comes to my mind is to use the Change event of the worksheet.
get rid of the checkboxes
Set the title of Column M to "Completed = X"
Use this code in the Table Object:
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Column = 13 Then
If uCase(Target.Value) = "X" Then
'--Write your copy-code here maybe ignore/delete the x first
MsgBox "CopyThat!"
End If
End If
End Sub
just a suggestion...

Multiple If Statements VBA

I'm very new to VBA and SQL, and I'm currently building a table to be uploaded to SQL onto Excel and using VBA.
I want to essentially say if column I(Check Market) or J(Check m2) have a value that says #NA then go no further and don't carry out the upload or the rest of the code. I think one of the problems might be I already have an IF loop - which is successful and has no errors associated with it.
This is my code so far
'Where Marked sht.Cells(Row,15) = "x" 'FIRST IF LOOP
If sht.Cells(lRow, 15) = "X" Then
'If I or J columns say #N/A then DO NOT continue
If IsError(sht.Cells(lRow, 9).Value) Then MsgBox "Error in Column 'Check Market'"
If IsError(sht.Cells(lRow, 10).Value) Then MsgBox "Error in Column 'Check m2'"
''''At the moment it is the above part that isn't successfully running, it notifies the user of an error but doesn't stop the process.
'Change blank spaces to be Null
*******
sSQL = *******Main part of code goes here******
'execute queries
********
'Put back all the 'null' values to blank
'''''
End If 'END OF IF X LOOP
I'm not clear if there's a possibility that both columns might have an error but I've provided for that just in case.
'If I or J say #N/A then dont proceed with upload.
If iserror(sht.Cells(lRow, 9).value) and iserror(sht.Cells(lRow, 10).value) Then
MsgBox "Errors in both Columns"
ElseIf iserror(sht.Cells(lRow, 9).value) Then
MsgBox "Error in Column 'Check Market'"
ElseIf iserror(sht.Cells(lRow, 10).value) Then
MsgBox "Error in Column 'Check KPI'"
Else: 'Continue
End if
Just adding to your original Code
I(9):Check Market J(10):Check m2
'If I or J say #N/A then dont proceed with upload.
If sht.Cells(lRow, 9).Value = "#N/A" Then
MsgBox "Error in Column 'Check Market'"
Exit Sub
ElseIf sht.Cells(lRow, 10).Value = "#N/A" Then
MsgBox "Error in Column 'Check KPI'"
Exit Sub
Else: 'Continue
Code for exiting with MsgBox
Dim response
response = MsgBox("My message here.", vbYesNo, "My Title")
If response = vbNo Then
Exit Sub
End If
MsgBox ("You clicked YES.")

My macro freezes after an undefined amount of iterations

I have a macro that checks if some names on column Q appear on column A (which is ordered alphabetically) and prints them out on column S if they do. However, every time I run it it freezes after an undefined amount of iterations (never on the same amount of iterations) so it's really hard to know what's going on. If I run it with a breaking point and press F5 for each iteration it doesn't freeze, the thing is I have thousands of names to compare and I really don't want to press F5 that many times.
Here's my code:
Sub test()
Range("Q2").Select
analizados = 0
falsos = 0
Do Until IsEmpty(ActiveCell)
id1 = ActiveCell.Value
primera = Left(id1, 1)
Range("A2").Select
Do While Not ActiveCell.Value Like "" & primera & "*"
ActiveCell.Offset(1, 0).Select
Loop
Do While ActiveCell.Value Like "" & primera & "*"
If id1 = ActiveCell.Value Then
Range("S2").Select
ActiveCell.Offset(falsos, 0).Select
ActiveCell.Value = id1
falsos = falsos + 1
Exit Do
End If
ActiveCell.Offset(1, 0).Select
Loop
analizados = analizados + 1
Range("Q2").Select
ActiveCell.Offset(analizados, 0).Select
Loop
End Sub
Thank you
As you noticed, it's not actually frozen. It's just that Excel can't keep up with updating the screen as fast as you're bombarding it with "something has changed on the active sheet" events, and at one point it gives up and lets the macro complete without bothering with refreshing - at least that's how I understand it (might not be exactly what's going on though).
Try this:
Sub Test()
On Error GoTo ErrHandler
Application.ScreenUpdating = False
'...
'(rest of your code)
'...
CleanExit:
Application.ScreenUpdating = True
Exit Sub
ErrHandler:
MsgBox Err.Description
Resume CleanExit
End Sub
Basically you tell Excel to not even bother with repainting itself until you're done: this should greatly speed up your loops.
You may want to combine this with different settings for Application.Calculation and Application.Cursor, too; and for a better UX you could use the status bar to tell the user to wait a little:
Sub Test()
On Error GoTo ErrHandler
Application.StatusBar = "Please wait..."
Application.ScreenUpdating = False
'...
'...
CleanExit:
Application.StatusBar = False
Application.ScreenUpdating = True
Exit Sub