main page >programming tips (VB5/6) >  upgraded Refresh method     diese Seite auf deutsch diese Seite auf deutsch
 
With a time-consuming procedure that is supposed to display its progress, you have to explicitly give Windows a chance to draw that progress display.

Example:

Private Sub Command1_Click()
Dim l As Long
  For l = 1 To 50000
    Label1.Caption = CStr(l)
  Next
End Sub
            
Running this code you will quickly notice that the Label doesn't display the current iteration at all. It works, however, with the following addition:

Private Sub Command1_Click()
Dim l As Long
  For l = 1 To 50000
    Label1.Caption = CStr(l)
    Label1.Refresh
  Next
End Sub
            
Instead of Label1.Refresh one could also use the DoEvents command; however, this would allow execution of all events, such as another click on the button which would start the routine for a second time (reentrancy). Hence precautions would be required to prevent this from happening. With .Refresh, you don't need any precautions.

The downside is that with Windows XP and later, this only works for a short time span; after a few seconds of being unresponsive, Windows assumes that the program is too busy to redraw itself, and thus creates a screenshot which is then displayed above the Form until the program responds again. While this may be nice in some situations, it ruins our efforts to dynamically display something on the screen. Now we could use DoEvents again to solve the problem, but as discussed above there are good reasons to avoid this.

Here comes a solution that replaces the Refresh method without any unwanted side effects:

Option Explicit

'Declarations (in a standard module)

Public Const WM_PAINT = &HF
Public Const PM_REMOVE = &H1

Public Type POINTAPI
  x As Long
  y As Long
End Type

Public Type MSG
  hwnd As Long
  message As Long
  wParam As Long
  lParam As Long
  time As Long
  pt As POINTAPI
End Type

Public Declare Function PeekMessage Lib "user32" Alias "PeekMessageA" _
  (lpMsg As MSG, ByVal hwnd As Long, ByVal wMsgFilterMin As Long, _
  ByVal wMsgFilterMax As Long, ByVal wRemoveMsg As Long) As Long
Public Declare Function TranslateMessage Lib "user32" _
  (lpMsg As MSG) As Long
Public Declare Function DispatchMessage Lib "user32" Alias "DispatchMessageA" _
  (lpMsg As MSG) As Long

'replacement for the Refresh method

Sub RefreshEx(ByVal hwnd As Long)
Dim mMSG As MSG
  If PeekMessage(mMSG, hwnd, WM_PAINT, WM_PAINT, PM_REMOVE) Then
    TranslateMessage mMSG
    DispatchMessage mMSG
  End If
End Sub
            
The parameter passed to this method - which, by the way, works on all Win32 versions before and after XP - is the window handle (hWnd) of the control or form to be redrawn. Since a label is windowless and hence has no window handle, we pass the handle of the owning form instead:

Private Sub Command1_Click()
Dim l As Long
  For l = 1 To 50000
    Label1.Caption = CStr(l)
    RefreshEx Me.hwnd
  Next
End Sub
            
main page >  programming tips (VB5/6) >  this page