C# Source Code: Re: Automatically setting the wait cursor for an application
[
Home
|
Contents
|
Search
|
Reply
|
Previous
|
Next
]
C# Source Code
Re: Automatically setting the wait cursor for an application
By:
Jeff Bramwell
Email (spam proof):
Email the originator of this post
Date:
Friday, March 10, 2006
Hits:
2779
Category:
Windows Forms/GUI/Controls
Article:
#Region " Imports..." Imports System Imports System.Diagnostics Imports System.Runtime.InteropServices Imports System.Threading Imports System.Windows.Forms #End Region '''
''' This static utility class can be used to automatically show a wait cursor when the application ''' is busy (ie not responding to user input). The class automatically monitors the application ''' state, removing the need for manually changing the cursor. '''
'''
''' To use, simply insert the following line in your Application startup code ''' ''' Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) ''' { ''' AutoWaitCursor.Cursor = Cursors.WaitCursor ''' AutoWaitCursor.Delay = new TimeSpan(0, 0, 0, 0, 25) ''' ''' 'Set the window handle to the handle of the main form in your application ''' AutoWaitCursor.MainWindowHandle = this.Handle ''' AutoWaitCursor.Start() ''' } ''' ''' This installs changes to cursor after 100ms of blocking work (ie. work carried out on the main application thread). ''' ''' Note, the above code GLOBALLY replaces the following: ''' ''' Public Sub DoWork() ''' Try ''' Screen.Cursor = Cursors.Wait ''' ''' GetResultsFromDatabase() ''' Finally ''' Screen.Cursor = Cursors.Default ''' End Try ''' End Sub '''
'''
''' This class was initially converted from a C# example provided by Andrew Baker at the following ''' URL:http://www.vbusers.com/codecsharp/codeget.asp?ThreadID=58&PostID=1&NumReplies=0 '''
_ Public Class AutoWaitCursor #Region " Private Attributes..." '''
''' The default TimeSpan to wait before showing the auto wait cursor. '''
Private Shared DEFAULT_DELAY As New TimeSpan(0, 0, 0, 0, 25) '''
''' The application state monitor class (which monitors the application busy status). '''
Private Shared _appStateMonitor As New ApplicationStateMonitor(Cursors.WaitCursor, DEFAULT_DELAY) #End Region #Region " Constructors..." '''
''' Initializes a new instance of the
class. '''
'''
Intentionally hidden.
Private Sub New() 'Do nothing End Sub #End Region #Region " Public Static Properties..." '''
''' Returns the amount of time the application has been idle. '''
Public ReadOnly Property ApplicationIdleTime() As TimeSpan Get Return _appStateMonitor.ApplicationIdleTime End Get End Property '''
''' Returns true if the auto wait cursor has been started. '''
Public Shared ReadOnly Property IsStarted() As Boolean Get Return _appStateMonitor.IsStarted End Get End Property '''
''' Gets or sets the Cursor to use during Application busy periods. '''
Public Shared Property Cursor() As Cursor Get Return _appStateMonitor.Cursor End Get Set(ByVal value As Cursor) _appStateMonitor.Cursor = value End Set End Property '''
''' Enables or disables the auto wait cursor. '''
Public Shared Property Enabled() As Boolean Get Return _appStateMonitor.Enabled End Get Set(ByVal value As Boolean) _appStateMonitor.Enabled = value End Set End Property '''
''' Gets or sets the period of Time to wait before showing the WaitCursor whilst Application is working '''
Public Shared Property Delay() As TimeSpan Get Return _appStateMonitor.Delay End Get Set(ByVal value As TimeSpan) _appStateMonitor.Delay = value End Set End Property '''
''' Gets or sets the main window handle of the application (ie the handle of an MDI form). ''' This is the window handle monitored to detect when the application becomes busy. '''
Public Shared Property MainWindowHandle() As IntPtr Get Return _appStateMonitor.MainWindowHandle End Get Set(ByVal value As IntPtr) _appStateMonitor.MainWindowHandle = value End Set End Property #End Region #Region " Public Methods..." '''
''' Starts the auto wait cursor monitoring the application. '''
Public Shared Sub Start() AutoWaitCursor.Start(AutoWaitCursor.MainWindowHandle) End Sub '''
''' Starts the auto wait cursor monitoring the application. '''
'''
Specifies the main window handle of the application ''' (ie the handle of an MDI form). Public Shared Sub Start(ByVal mainWindowHandle As IntPtr) AutoWaitCursor.MainWindowHandle = mainWindowHandle _appStateMonitor.Start() End Sub '''
''' Stops the auto wait cursor monitoring the application. '''
Public Shared Sub [Stop]() _appStateMonitor.Stop() End Sub #End Region #Region " Private Class ApplicationStateMonitor..." '''
''' Private class that monitors the state of the application and automatically ''' changes the cursor accordingly. '''
Private Class ApplicationStateMonitor : Implements IDisposable #Region " Private Attributes..." '''
''' The time the application became inactive. '''
Private _inactiveStart As DateTime = DateTime.Now '''
''' If the monitor has been started. '''
Private _isStarted As Boolean = False '''
''' Delay to wait before calling back '''
Private _delay As TimeSpan '''
''' The windows handle to the main process window. '''
Private _mainWindowHandle As IntPtr = IntPtr.Zero '''
''' Thread to perform the wait and callback '''
Private _callbackThread As Thread = Nothing '''
''' Stores if the class has been disposed of. '''
Private _isDisposed As Boolean = False '''
''' Stores if the class is enabled or not. '''
Private _enabled As Boolean = True '''
''' GUI Thread Id . '''
Private _mainThreadId As System.UInt32 'ToDo: Unsigned Integers not supported '''
''' Callback Thread Id. '''
Private _callbackThreadId As System.UInt32 'ToDo: Unsigned Integers not supported '''
''' Stores the old cursor. '''
Private _oldCursor As Cursor '''
''' Stores the new cursor. '''
Private _waitCursor As Cursor #End Region #Region " Constants..." Private Const SMTO_NORMAL As Integer = &H0 Private Const SMTO_BLOCK As Integer = &H1 Private Const SMTO_NOTIMEOUTIFNOTHUNG As Integer = &H8 #End Region #Region " API (PInvoke) Declarations..."
_ Private Shared Function SendMessageTimeout(ByVal hWnd As IntPtr, ByVal Msg As Integer, ByVal wParam As Integer, ByVal lParam As String, ByVal fuFlags As Integer, ByVal uTimeout As Integer, ByRef lpdwResult As Integer) As
Boolean End Function
_ Private Shared Function AttachThreadInput(ByVal attachTo As System.UInt32, ByVal attachFrom As System.UInt32, ByVal attach As Boolean) As System.UInt32 End Function
_ Private Shared Function GetCurrentThreadId() As System.UInt32 'ToDo: Unsigned Integers not supported End Function #End Region #Region " Constructors..." '''
''' Default member initialising Constructor. '''
'''
The wait cursor to use. '''
The delay before setting the cursor to the wait cursor. Public Sub New(ByVal waitCursor As Cursor, ByVal delay As TimeSpan) 'Constructor is called from (what is treated as) the main thread _mainThreadId = GetCurrentThreadId() _delay = delay _waitCursor = waitCursor 'Gracefully shuts down the state monitor AddHandler Application.ThreadExit, AddressOf OnApplicationThreadExit End Sub 'New #End Region #Region " IDisposable Interface Methods..." '''
''' On Disposal terminates the Thread, calls Finish (on thread) if Start has been called '''
Public Sub Dispose() Implements System.IDisposable.Dispose If (_isDisposed) Then Return End If 'Kills the Thread loop _isDisposed = True End Sub 'Dispose #End Region #Region " Public Properties..." '''
''' Returns true if the auto wait cursor has been started. '''
Public ReadOnly Property IsStarted() As Boolean Get Return _isStarted End Get End Property '''
''' Gets or sets the main window handle of the application (ie the handle of an MDI form). ''' This is the window handle monitored to detect when the application becomes busy. '''
Public Property MainWindowHandle() As IntPtr Get Return _mainWindowHandle End Get Set(ByVal value As IntPtr) _mainWindowHandle = value End Set End Property '''
''' Gets or sets the Cursor to show. '''
Public Property Cursor() As Cursor Get Return _waitCursor End Get Set(ByVal value As Cursor) _waitCursor = value End Set End Property '''
''' Returns the amount of time the application has been idle. '''
Public ReadOnly Property ApplicationIdleTime() As TimeSpan Get Return DateTime.Now.Subtract(_inactiveStart) End Get End Property #End Region #Region " Public Methods..." '''
''' Starts the application state monitor. '''
Public Sub Start() If (Not _isStarted) Then _isStarted = True Me.CreateMonitorThread() End If End Sub '''
''' Stops the application state monitor. '''
Public Sub [Stop]() If (_isStarted) Then _isStarted = False End If End Sub '''
''' Set the Cursor to wait. '''
Public Sub SetWaitCursor() 'Start is called in a new Thread, grab the new Thread Id so we can attach to Main thread's input _callbackThreadId = GetCurrentThreadId() 'Have to call this before calling Cursor.Current AttachThreadInput(_callbackThreadId, _mainThreadId, True) _oldCursor = Windows.Forms.Cursor.Current Windows.Forms.Cursor.Current = _waitCursor End Sub '''
''' Finish showing the Cursor (switch back to previous Cursor). '''
Public Sub RestoreCursor() 'Restore the cursor Windows.Forms.Cursor.Current = _oldCursor 'Detach from Main thread input AttachThreadInput(_callbackThreadId, _mainThreadId, False) End Sub '''
''' Enable/Disable the call to Start (note, once Start is called it *always* calls the paired Finish). '''
Public Property Enabled() As Boolean Get Return _enabled End Get Set(ByVal value As Boolean) _enabled = value End Set End Property '''
''' Gets or sets the period of Time to wait before calling the Start method. '''
Public Property Delay() As TimeSpan Get Return _delay End Get Set(ByVal value As TimeSpan) _delay = value End Set End Property #End Region #Region " Private Methods..." '''
''' Prepares the class creating a Thread that monitors the main application state. '''
Private Sub CreateMonitorThread() 'Create the monitor thread _callbackThread = New Thread(New ThreadStart(AddressOf ThreadCallbackLoop)) _callbackThread.Name = "AutoWaitCursorCallback" _callbackThread.IsBackground = True 'Start the thread _callbackThread.Start() End Sub '''
''' Thread callback method. ''' Loops calling SetWaitCursor and RestoreCursor until Disposed. '''
Private Sub ThreadCallbackLoop() Try Do If Not _enabled OrElse _mainWindowHandle = IntPtr.Zero Then 'Just sleep Thread.Sleep(_delay) Else 'Wait for start If IsApplicationBusy(_delay, _mainWindowHandle) Then Try Me.SetWaitCursor() WaitForIdle() Finally ' Always calls Finish (even if we are Disabled) Me.RestoreCursor() ' Store the time the application became inactive _inactiveStart = DateTime.Now End Try Else ' Wait before checking again Thread.Sleep(25) End If End If Loop While Not _isDisposed AndAlso _isStarted Catch 'The thread is being aborted, just reset the abort and exit gracefully Thread.ResetAbort() End Try End Sub '''
''' Blocks until the application responds to a test message. ''' If the application doesn't respond with the timespan, will return false, ''' else returns true. '''
Private Function IsApplicationBusy(ByVal delay As TimeSpan, ByVal windowHandle As IntPtr) As Boolean Const INFINITE As Integer = Int32.MaxValue Const WM_NULL As Integer = 0 Dim result As Integer = 0 Dim success As Boolean 'See if the application is responding If (delay = TimeSpan.MaxValue) Then success = SendMessageTimeout(windowHandle, WM_NULL, 0, Nothing, SMTO_BLOCK, INFINITE, result) Else success = SendMessageTimeout(windowHandle, WM_NULL, 0, Nothing, SMTO_BLOCK, System.Convert.ToInt32(delay.TotalMilliseconds), result) End If If result <> 0 Then Return True End If Return False End Function '''
''' Waits for the ResetEvent (set by Dispose and Reset), ''' since Start has been called we *have* to call RestoreCursor once the thread is idle again. '''
Private Sub WaitForIdle() 'Wait indefinately until the application is idle IsApplicationBusy(TimeSpan.MaxValue, _mainWindowHandle) End Sub '''
''' The application is closing, shut the state monitor down. '''
'''
The sender. '''
The
instance containing the event data. Private Sub OnApplicationThreadExit(ByVal sender As Object, ByVal e As EventArgs) Me.Dispose() End Sub #End Region End Class #End Region End Class
Terms and Conditions
Support this site
Download a trial version of the best FTP application on the internet