My macro freezes after an undefined amount of iterations - vba

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

Related

My code does not execute

I run the following code and VBA just flashes for a millisecond and no results are given. No matter how I edit the code, VBA would not execute it.
I am so confused. However, if I run my original code, it works...I try making the same edits to my original code and VBA would run but would stop running after a few tries.
Does anyone know what the heck is going on?
Start Code:
Sub LeadDetailsQR()
Dim OgData As String
OgData = ActiveSheet.Name
Sheets(OgData).AutoFilterMode = False
varMyData = Sheets(OgData).Range("AK2", Range("AK" & Rows.Count).End(xlUp)).Value
With CreateObject("scripting.dictionary")
For Each varItem In varMyData
If Not IsEmpty(varItem) Then .Item(varItem) = Empty
Next varItem
For Each varItem In .keys
Cells.AutoFilter
Sheets.Add Before:=ActiveSheet
Application.DisplayAlerts = False
On Error Resume Next
ActiveWorkbook.Worksheets(varItem).Delete
On Error GoTo 0
Application.DisplayAlerts = True
ActiveSheet.Name = varItem
Sheets(OgData).Select
Sheets(OgData).Range("AK1").AutoFilter Field:=37, Criteria1:=varItem
Sheets(OgData).Cells.CurrentRegion.Copy
Sheets(varItem).Cells.PasteSpecial Paste:=xlPasteColumnWidths
Sheets(OgData).Cells.CurrentRegion.Copy
Sheets(varItem).Cells.PasteSpecial Paste:=xlPasteAll
Next varItem
End With
Sheets(OgData).AutoFilterMode = False
End Sub
Orginal code (works):
Sub LeadDetailsQROriginal()
Dim OgData As String
OgData = ActiveSheet.Name
Sheets(OgData).AutoFilterMode = False
varMyData = Sheets(OgData).Range("A2", Range("A" & Rows.Count).End(xlUp)).Value
With CreateObject("scripting.dictionary")
For Each varItem In varMyData
If Not IsEmpty(varItem) Then .Item(varItem) = Empty
Next varItem
For Each varItem In .keys
Sheets.Add Before:=ActiveSheet
ActiveSheet.Name = varItem
Sheets(OgData).Range("A1").AutoFilter Field:=1, Criteria1:=varItem
Sheets(OgData).Select
Sheets(OgData).Range("A1").CurrentRegion.Copy
Sheets(varItem).Range("A1").PasteSpecial Paste:=xlPasteColumnWidths
Sheets(OgData).Range("A1").CurrentRegion.Copy
Sheets(varItem).Range("A1").PasteSpecial Paste:=xlPasteAll
Next varItem
End With
Sheets(OgData).AutoFilterMode = False
End Sub
I am embarrassed to say that I have found the answer. The code is doing exactly what it is doing. It ran without error because the range it was looking for is blank and as a result, nothing is created.
If I am not being clear, let me explain the code. The code is supposed to create a new sheet based on each unique value in a column. The code ran without producing any results because I was asking the code to look at column AK, which is a blank column. So of course it didn't produce anything :)

MsgBox appears multiple times after data is pulled from a sheet

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

Full Screen Coding

I have the following code that loads a worksheet in full screen for 1 minute, and then moves onto the next worksheet in the workbook, using the exactly the same methodology.
This is to show stats on a big screen, looping through several stats pages.
This works perfectly on Excel 2007 and 2010.
Yet when the same code is executed on Excel 2013, Excel simply maxes out 1 core of my CPU and stays at not responding. I cannot even Escape to break the code execution. Stepping through the code line by line works fine on all versions.
'Loads up Daily Dispatch Figures worksheet
Application.ScreenUpdating = False
Sheets("Daily Dispatch Figures").Select
Range("A1").Select
Range("A1:C36").Select
ActiveWindow.Zoom = True
Range("A1").Select
ActiveWindow.DisplayHeadings = False
Application.DisplayFormulaBar = False
Application.DisplayFullScreen = True
Application.ScreenUpdating = True
' Stays on this screen for 1 min
TimVal = Now + TimeValue("0:01:00")
Do Until Now >= TimVal
Loop
Ooo, don't do this:
' Stays on this screen for 1 min
TimVal = Now + TimeValue("0:01:00")
Do Until Now >= TimVal
Loop
Try this:
Application.OnTime Now + TimeValue("0:01:00"), "ProcedureToRun"
You don't want to catch your application in an infinite loop with no sleeps.
Any time you sit in an infinite loop without sleeping, it will use 100% of your Processor time doing nothing. Application.OnTime "schedules" an event and returns control to the Excel UI Thread instead of infinitely looping.
You can read more here: https://msdn.microsoft.com/en-us/library/office/ff196165.aspx
I'm not sure what you're doing after your loop, but you need to make sure you have the code in a separate subroutine and call it.
Here is a Subroutine to go to the next sheet.
Sub MoveNext()
On Error Resume Next
Sheets(ActiveSheet.Index + 1).Activate
If Err.Number <> 0 Then Sheets(1).Activate
On Error Goto 0
End Sub
You can add the Application.OnTime to the end of it and have it call itself:
Sub MoveNext()
On Error Resume Next
Sheets(ActiveSheet.Index + 1).Activate
If Err.Number <> 0 Then Sheets(1).Activate
On Error Goto 0
Application.OnTime Now + TimeValue("00:01:00"), MoveNext
End Sub
This way it will loop and go from sheet to sheet forever (or until you stop it in whatever method you choose to use).
Finally, you can cancel this by storing the scheduled time and using Scheduled:=False.
Your final code could look something like this:
Public scheduledTime as Date
Sub StartDisplaying()
'Your start code:
'---------------------------------------------
Application.ScreenUpdating = False
Sheets("Daily Dispatch Figures").Select
Range("A1").Select
Range("A1:C36").Select
ActiveWindow.Zoom = True
Range("A1").Select
ActiveWindow.DisplayHeadings = False
Application.DisplayFormulaBar = False
Application.DisplayFullScreen = True
Application.ScreenUpdating = True
'---------------------------------------------
scheduledTime = Now + TimeValue("00:01:00")
Application.OnTime scheduledTime, MoveNext
End Sub
Sub StopDisplaying()
'Your stop code:
'---------------------------------------------
Application.ScreenUpdating = False
Sheets("Daily Dispatch Figures").Select
ActiveWindow.Zoom = False
ActiveWindow.DisplayHeadings = True
Application.DisplayFormulaBar = True
Application.DisplayFullScreen = False
Application.ScreenUpdating = True
'---------------------------------------------
Application.OnTime EarliestTime:=scheduledTime, Procedure:="MoveNext", Schedule:=False
End Sub
Sub MoveNext()
On Error Resume Next
Sheets(ActiveSheet.Index + 1).Activate
If Err.Number <> 0 Then Sheets(1).Activate
On Error Goto 0
scheduledTime = Now + TimeValue("00:01:00")
Application.OnTime scheduledTime, MoveNext
End Sub

Progress bar for consolidation loop in VBA

I have this consolidation macro which opens, copies and pastes data from one sheet of several workbooks onto a master sheet where such data as well as workbooks maybe in the thousands. Overall this process will take anywhere from 30mins to an hour and I thought a progress bar would help.
I got the code i used for the consolidation part here at stackoverflow. It was somebody with a similar issue, however, the progress bar code i got somewhere else. I had to jury-rig the code of sorts to fit it for my needs.. The examples online uses a for next loop code for the progress bar which mine doesn't.
i tried running my code but the progress bar doesn't update.. T_T
can somebody help me with what's wrong with my code?
Any help on this is very much appreciated.. thanks..
Sub OpeningFiles()
Dim SelectedFiles As FileDialog
Dim NumFiles As Long, FileIndex As Long
Dim TargetBook As Workbook
Dim sName, sName2, sName3 As Range
Dim pctCompl As Single
Set sName = ThisWorkbook.Sheets("Sheet1").Range("j1")
Set SelectedFiles = Application.FileDialog(msoFileDialogOpen)
With SelectedFiles
.AllowMultiSelect = True
.Title = "Pick the files you'd like to consolidate:"
.ButtonName = ""
.Filters.Clear
.Filters.Add ".xlsx files", "*.xlsx"
.Show
End With
If SelectedFiles.SelectedItems.Count = 0 Then Exit Sub
NumFiles = SelectedFiles.SelectedItems.Count
For FileIndex = 1 To NumFiles
Set TargetBook = Workbooks.Open(SelectedFiles.SelectedItems(FileIndex), ReadOnly:=True)
Application.DisplayAlerts = False
ActiveWorkbook.Activate
Sheets(sName).Activate
On Error GoTo 0
Range("d11:j11").Select
Range(Selection, Selection.End(xlDown)).Copy
ThisWorkbook.Sheets("Sheet1").Activate
Range("b2").Select
Do
If IsEmpty(ActiveCell) = False Then
ActiveCell.Offset(1, 0).Select
End If
Loop Until IsEmpty(ActiveCell) = True
ActiveCell.PasteSpecial Paste:=xlPasteValues
ThisWorkbook.Application.CutCopyMode = False
TargetBook.Close SaveChanges:=False
Next FileIndex
progress pctCompl
MsgBox ("Consolidation complete!")
End Sub
Sub progress(pctCompl As Single)
UserForm1.Text.Caption = pctCompl & "% Completed"
UserForm1.Bar.Width = pctCompl * 2
DoEvents
End Sub
Sub ShowProgress()
UserForm1.Show
End Sub
Addendum:
This code
Sheets(sName).activate
selects the sheetname of the opened file wherein it is always a number from 1-30. Right now, I have to indicate that number one at a time. Is there a way to do it like 3 or 7 times? like a loop? e.g 1-7 or 25-27.. It is always ascending so i thought a code like the one below will work? Thoughts?
For sName = sNameStart To sNameEnd Step 1
Sheets(sName).Activate
On Error GoTo 0
Range("d11:j11").Select
Range(Selection, Selection.End(xlDown)).Copy
ThisWorkbook.Sheets("Sheet1").Activate
Range("b2").Select
Do
If IsEmpty(ActiveCell) = False Then
ActiveCell.Offset(1, 0).Select
End If
Loop Until IsEmpty(ActiveCell) = True
ActiveCell.PasteSpecial Paste:=xlPasteValues
ActiveWorkbook.Activate
Next sName
where sName is the sheet name, sNameStart is the start sheet and sNameEnd is the end sheet.
However, i get this error when I start this code.. Help?
You need to move your call to progress pctCompl inside your loop.
The code you posted doesn't call progress pctCompl until after Next FileIndex
ThisWorkbook.Application.CutCopyMode = False
TargetBook.Close SaveChanges:=False
Next FileIndex
progress pctCompl
MsgBox ("Consolidation complete!")
Replace it with this:
ThisWorkbook.Application.CutCopyMode = False
TargetBook.Close SaveChanges:=False
'insert your command here
progress pctCompl
Next FileIndex
MsgBox ("Consolidation complete!")
If you need something more precise than progress bar try putting:
Application.StatusBar = "File " & FileIndex & " of " & NumFiles
somewhere within For..Next loop, I like this because it is more verbose than just progress bar.
And remember to put
Application.StatusBar = False
After your loop to restore standard status bar.

Variables not setting between cases?

I have the following code:
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Cells.Count > 1 Then Exit Sub
On Error Resume Next
Dim StartBox As Long
Dim StartBox2 As Long
Select Case UCase(Target.Value)
Case "NEW-BOX"
StartBox = ActiveCell.Row
StartBox2 = ActiveCell.Column
MsgBox (StartBox)
MsgBox (StartBox2)
Selection.Offset(-1, 2).Select
Selection.ClearContents
Selection.Activate
Selection.Offset(1, -2).Select
Case "RESTART-BOX"
MsgBox (StartBox)
MsgBox (StartBox2)
If StartBox = 0 And StartBox2 = 0 Then
MsgBox "Cannot restart box without scanning a new box first!", vbCritical
ElseIf StartBox <> 0 And StartBox2 <> 0 Then
ActiveSheet.Range(Cells(StartBox, StartBox2), Cells(ActiveCell.Row, ActiveCell.Column)).ClearContents
End If
End Select
End Sub
I scan a new box, and the variables set to the correct columns and row, but when I scan restart box, the message boxes both come up 0? Why is this? I need to pass these variables onto my code to clear the contents, but for some reason even though I am setting them they won't appear in 'RESTART-BOX' ?
We need more context to be able to provide a definitive answer. Is your code in a loop of some sort or is this a sub or function being called multiple times?
If the latter then you will get a new copy of StartBox and StartBox2 created each time you call the sub / function, so they won't retain the values. If you place the dim statements outside the sub or function then they will become global variables and will retain their values across each call to the sub or function.
You'll need to set your 2 variables before your select statement:
...
StartBox = ActiveCell.Row
StartBox2 = ActiveCell.Column
Select Case UCase(Target.Value)
...