|
|
Title | Flush click events to prevent the user from clicking a button while its code is still running in Visual Basic .NET |
Description | This example shows how to flush click events to prevent the user from clicking a button while its code is still running in Visual Basic .NET. |
Keywords | syntax, API, threading, threads, BackgroundWorker, controls, events, flush events, flush mouse events, Visual Basic .NET, VB.NET |
Categories | Controls, Miscellany, Software Engineering |
|
|
If a button starts a long task, you probably don't want the user to be able to click the button again (or perhaps not anything on the application) until the task finishes. The following code shows a straightforward attempt to prevent the user from clicking the button while its code is still executing.
|
|
' Wait for 5 seconds.
Private Sub btnWaitNow_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnWaitNow.Click
' None of these seem to work.
'Me.Enabled = False
'RemoveHandler btnWaitNow.Click, AddressOf
' btnWaitNow_Click
btnWaitNow.Enabled = False
Me.Cursor = Cursors.WaitCursor
Application.DoEvents()
lstMessages.Items.Add("Wait Now Start " + _
DateTime.Now.ToString())
Refresh()
System.Threading.Thread.Sleep(5000)
lstMessages.Items.Add("Wait Now Stop " + _
DateTime.Now.ToString())
'Me.Enabled = True
'AddHandler btnWaitNow.Click, AddressOf btnWaitNow_Click
btnWaitNow.Enabled = True
Me.Cursor = Cursors.Default
End Sub
|
|
When the event handler starts, it disables the button. It then does its work and re-enables the button.
Unfortunately this approach doesn't work. Windows very helpfully queues up any pending mouse events including clicks while your program is busy and then delivers them when the event handler finishes so you can receive the second click. (I could have sworn this approach used to work.)
One way around this is to use a BackgroundWorker or other threading technique to perform the work on a separate thread. Disable the button and then start the thread. When the thread finishes, re-enable the button. This method works and may have other advantages (such as allowing the user to interact with other parts of the program while the button's task is still running), but it's a bit roundabout.
Another approach is to use the PeekMessage API function, as shown in the following code.
|
|
<StructLayout(LayoutKind.Sequential)> _
Private Structure NativeMessage
Public handle As IntPtr
Public msg As UInteger
Public wParam As IntPtr
Public lParam As IntPtr
Public time As UInteger
Public p As System.Drawing.Point
End Structure
Private Declare Auto Function PeekMessage Lib "user32.dll" ( _
_
ByRef lpMsg As NativeMessage, _
ByVal hWnd As IntPtr, _
ByVal wMsgFilterMin As UInteger, _
ByVal wMsgFilterMax As UInteger, _
ByVal flags As UInteger _
) As Boolean
Private Const WM_MOUSEFIRST As UInteger = &H200
Private Const WM_MOUSELAST As UInteger = &H20D
Private Const PM_REMOVE As Integer = &H1
' Flush all pending mouse events.
Private Sub FlushMouseMessages()
Dim msg As NativeMessage
' Repeat until PeekMessage returns false.
While (PeekMessage(msg, IntPtr.Zero, WM_MOUSEFIRST, _
WM_MOUSELAST, PM_REMOVE))
End While
End Sub
|
|
This code includes a bunch of declarations for the API function and its parameters. (You also need to add using statements for the System.Runtime.InteropServices and System.Security namespaces. Download the example for the details.)
The FlushMouseMessages method calls PeekMessage telling it to discard any message between WM_MOUSELAST and PM_REMOVE. It calls PeekMessage repeatedly until it returns false to indicate that there are no such messages.
The following button event handler calls FlushMouseMessage so you cannot click the button while its code is still running.
|
|
' Wait for 5 seconds and then flush the buffer.
Private Sub btnWaitAndFlush_Click(ByVal sender As _
System.Object, ByVal e As System.EventArgs) Handles _
btnWaitAndFlush.Click
Me.Cursor = Cursors.WaitCursor
lstMessages.Items.Add("Wait and Flush Start " + _
DateTime.Now.ToString())
Refresh()
System.Threading.Thread.Sleep(5000)
lstMessages.Items.Add("Wait and Flush Stop " + _
DateTime.Now.ToString())
FlushMouseMessages()
Me.Cursor = Cursors.Default
End Sub
|
|
|
|
|
|
|
|
|