DoEvents vs. Repaint in Microsoft Access
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.
Updated demo
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
Referenced articles
External references
Image by Prashant Sharma from Pixabay