This is bonus 1 of a multi-part series on the DoEvents function.
In part 3 of my series of articles exploring the DoEvents function, I showed the following demonstration of the difference between running a loop without a DoEvents call vs. one with a DoEvents call.
Microsoft Access is single-threaded. The code and the user interface both run on the same thread. If you want to provide form updates during a long-running process (like for a custom progress meter form), you need to explicitly give the UI a chance to update itself.
Question from Joakim Dalby
On LinkedIn, Joakim Dalby asked a follow-up question about that article:
Could Me.Refresh or Me.Repaint or Me.Recalc replace your DoEvents in your test form?
My response on LinkedIn
I just tested these three options using my demo form (https://nolongerset.com/doevents-demo/).
Me.Refresh had no effect.
Me.Recalc resulted in the Status Bar showing "Calculating...", but had no other obvious effect.
Me.Repaint *did* result in the UI being updated. However, it did not allow the Cancel button to work.
So, in certain situations, Me.Repaint could be preferable to DoEvents, as it is more precise in what it does. Good call!
The Form Repaint Method
From the official Microsoft docs:
The Repaint method completes any pending screen updates for a specified form. When performed on a form, the Repaint method also completes any pending recalculations of the form's controls.
The documentation goes on to clarify the difference between the Repaint method and the Refresh method:
Don't confuse the Repaint method with the Refresh method, or with the Refresh command on the Records menu. The Refresh method and the Refresh command show changes that you or other users have made to the underlying record source for any of the currently displayed records in forms and datasheets. The Repaint method simply updates the screen when repainting has been delayed while Access completes other tasks.
The Recalc method simply forces a recalculation of any calculated controls.
I updated my previous demonstration to show the effect of calling Me.Repaint instead of DoEvents within the loop.
Here is the code-behind for the above form:
Option Compare Database Option Explicit #If VBA7 Then Private Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) #Else Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) #End If Private mUserCanceled As Boolean Private Sub RunLoop(YieldCommand As String) mUserCanceled = False DoCmd.Hourglass True Me.tbCount.Value = 0 Dim i As Long For i = tbStart.Value To tbEnd.Value If mUserCanceled Then Exit For Sleep tbDelay.Value Me.tbCount.Value = i Select Case YieldCommand Case "Nothing" 'Do nothing to yield execution Case "Repaint" Me.Repaint Case "DoEvents" DoEvents End Select Next i DoCmd.Hourglass False End Sub Private Sub btnNoDoEvents_Click() RunLoop "Nothing" End Sub Private Sub btnRepaint_Click() RunLoop "Repaint" End Sub Private Sub btnWithDoEvents_Click() RunLoop "DoEvents" End Sub Private Sub btnCancel_Click() mUserCanceled = True End Sub
If you want to play with the code yourself, there is a sample database available here: TestDoEvents-v2.zip