none
UseWaitCursor not working but Cursor = Cursors.WaitCursor does...

    Question

  • I'm trying to make use of the UseWaitCursor property of a control - in this case the main Form of my app. I just thought I'd try and make it have a little more feedback.
     
    All I've done is this:
    void someEventHandler(...)
    {
        main_form->UseWaitCursor = true;
        <
            Do something really long
        >
        main_form->UseWaitCursor = false;
    }

    But unfortunately that doesn't seem to work quite as I expect.
    I know that UseWaitCursor works ok by itself because if I don't set it back to false at the end of the function I will get the hourglass.
     
    The problem is that it doesn't seem to change to an hourglass until the function returns - by which time its to late.
     
    I also tried to use the System.Windows.Forms.Application.UseWaitCursor but it has the same issue.

    But if I change the main_form->UseWaitCursor = true to main_form->Cursor = Cursors.WaitCursor then it works fine.

    Any ideas why?

    Wednesday, January 25, 2006 10:01 PM

Answers

  • Yes, WF's implementation of UseWaitCursor missed an opportunity to make it actually work to show an hourglass cursor.  This class may solve your problem:

    using System;
    using System.Windows.Forms;

    public class HourGlass : IDisposable {
      public HourGlass() {
        Enabled = true;
      }
      public void Dispose() {
        Enabled = false;
      }
      public static bool Enabled {
        get { return Application.UseWaitCursor; }
        set {
          if (value == Application.UseWaitCursor) return;
          Application.UseWaitCursor = value;
          Form f = Form.ActiveForm;
          if (f != null && f.Handle != null)   // Send WM_SETCURSOR
            SendMessage(f.Handle, 0x20, f.Handle, (IntPtr)1);
        }
      }
      [System.Runtime.InteropServices.DllImport("user32.dll")]
      private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
    }

    You can use it either directly by assigning HourGlass.Enabled or like this:

        private void button1_Click(object sender, EventArgs e) {
          using (new HourGlass()) {
            // Do something that takes time...
            System.Threading.Thread.Sleep(2000);
          }
        }

    The "using" keyword ensures the hourglass cursor disappears, no matter what goes wrong.
    Wednesday, June 27, 2007 3:57 PM

All replies

  • UseWaitCursor seems to be useful only for long running operations that continue to process Windows messages.

    I gues that what it does is have the window handle the WM_SETCURSOR message and change the cursor to the hourglass.

    For the more traditional long running process (that doesn't process Windows messages), you have to change the cursor explicitly as you've found.

    It would have been nice if the .Net documentation mentioned this!

    Monday, February 20, 2006 12:13 PM
  • The problem with setting Form.Cursor is that it does not set the Cursor for its child controls, meaning you get the input cursor when you hover over a text box etc
    Wednesday, June 27, 2007 2:46 AM
  • Yes, WF's implementation of UseWaitCursor missed an opportunity to make it actually work to show an hourglass cursor.  This class may solve your problem:

    using System;
    using System.Windows.Forms;

    public class HourGlass : IDisposable {
      public HourGlass() {
        Enabled = true;
      }
      public void Dispose() {
        Enabled = false;
      }
      public static bool Enabled {
        get { return Application.UseWaitCursor; }
        set {
          if (value == Application.UseWaitCursor) return;
          Application.UseWaitCursor = value;
          Form f = Form.ActiveForm;
          if (f != null && f.Handle != null)   // Send WM_SETCURSOR
            SendMessage(f.Handle, 0x20, f.Handle, (IntPtr)1);
        }
      }
      [System.Runtime.InteropServices.DllImport("user32.dll")]
      private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
    }

    You can use it either directly by assigning HourGlass.Enabled or like this:

        private void button1_Click(object sender, EventArgs e) {
          using (new HourGlass()) {
            // Do something that takes time...
            System.Threading.Thread.Sleep(2000);
          }
        }

    The "using" keyword ensures the hourglass cursor disappears, no matter what goes wrong.
    Wednesday, June 27, 2007 3:57 PM
  • That looks like it could be useful, ill need to convert it into VB.NET but that should be alright...

     

    Thanks for that.

    Wednesday, June 27, 2007 11:25 PM
  •  

    Here's the above code rewritten in VB.NET 2005, if anyone needs it:

     

     

    Public Class HourGlass

    Implements IDisposable

     

    Private disposedValue As Boolean = False ' To detect redundant calls

     

    Public Sub New()

    Enabled = True

    End Sub

     

    Public Property Enabled() As Boolean

    Get

    Return Application.UseWaitCursor

    End Get

    Set(ByVal value As Boolean)

    If value = Application.UseWaitCursor Then Return

    Application.UseWaitCursor = value

    Dim f As Form = Form.ActiveForm

    If (Not (f Is Nothing)) AndAlso (Not (f.Handle = vbNull)) Then

    SendMessage(f.Handle, &H20, f.Handle, 1)

    End If

    End Set

    End Property

     

    ' IDisposable

    Protected Overridable Sub Dispose(ByVal disposing As Boolean)

    If Not Me.disposedValue Then

    Enabled = False

    End If

    Me.disposedValue = True

    End Sub

     

    #Region " IDisposable Support "

    ' This code added by Visual Basic to correctly implement the disposable pattern.

    Public Sub Dispose() Implements IDisposable.Dispose

    ' Do not change this code.

    ' Put cleanup code in Dispose(ByVal disposing As Boolean) above.

    Dispose(True)

    GC.SuppressFinalize(Me)

    End Sub

    #End Region

     

    End Class

     

     

    And the API call (I always keep them in a separate module):

     

     

    <DllImport("user32")> _

    Public Function SendMessage( _

    ByVal hWnd As Integer, _

    ByVal msg As UInteger, _

    ByVal wp As Integer, _

    ByVal lp As Integer) _

    As Integer

    End Function

    Thursday, October 25, 2007 10:48 PM
  • I had the same problem (no hourglass when I set UseWaitCursor = true;), and your C# class solved it for me.  Thanks!
    Sunday, April 12, 2009 8:26 PM
  • Another solution is to adjust the current cursor. But be sure to reset the cursor in the same method to avoid a "persistent" wait cursor.

    Cursor previousCursor = Cursor.Current;
    Cursor.Current = Cursors.WaitCursor;
    
    // Do something
    
    Cursor.Current = previousCursor;
    
    Wednesday, April 22, 2009 8:25 AM
  • Sweet.  It solved my issue, although I don't understand WHY I had an issue.  Perhaps you can enlighten me.

    Using VS2008, building for Framework 2.0.  This code excerpt follows the return from a FileOpen dialog:
    if (DialogResult.OK == res)
    {
        if (txtWvsFilename.Text != dlgOpenWvsFile.FileName)
        {
            lblStatus.Text = "Loading data...";
            this.Cursor = Cursors.WaitCursor;
            this.Update();
    

    What follows is a long activity reading the file content.  I know I need the Update (or a Refresh) call to get the changed label text to show, but that same call seems to cancel the cursor change.  I tried changing the cursor on both sides of the Update call with no difference in results. 

    If I remove the Update call, the WaitCursor shows, but the form doesn't repaint the child controls that the FileOpen dialog had covered. That leads me to believe the cursor change I see is not one I initiated, but one initiated internally within Windows.

    (But it works perfectly using your HourGlass class!)

    Friday, July 24, 2009 7:55 PM
  • I combined the two approaches (a class and Cursor.Current) :

    public class WaitCursor : IDisposable
    {
        Cursor m_cursorOld;
    
        public WaitCursor()
        {
            m_cursorOld = Cursor.Current;
            Cursor.Current = Cursors.WaitCursor;
        }
        public void Dispose()
        {
            Cursor.Current = m_cursorOld;
        }
    }
    
    


    Then to use the class you just wrap the slow code in a using block:
    using (new WaitCursor())
    {
      // slow stuff
    }
    
    Sunday, July 26, 2009 6:56 PM
  • Complete solution in C#.NET :

      public class WaitCursor : IDisposable
      {
        Cursor m_cursorOld;
        bool _disposedValue = false; // To detect redundant call 
    
        /// <summary>
        /// .cteur
        /// </summary>
        public WaitCursor()
        {
          m_cursorOld = Cursor.Current;
          Cursor.Current = Cursors.WaitCursor;
        }
    
        /// <summary>
        /// Dispose
        /// </summary>
        public void Dispose()
        {
          Dispose(true);
          GC.SuppressFinalize(this);
        }
    
        /// <summary>
        /// Dispose
        /// </summary>
        /// <param name="disposing"></param>
        protected virtual void Dispose(bool disposing)
        {
          if (!_disposedValue)
            Cursor.Current = m_cursorOld;
          _disposedValue = true;
        }
    
      }

    Then, use as follow:

    using (new WaitCursor())
    {
     // Slow code
    }

     

    Tuesday, May 04, 2010 9:03 AM
  • You can still use UseWaitCursor=true, and then, to when you're finished, use:

    UseWaitCursor=false

    Me.Cursor = Me.Cursor

     

    Or, if you want:

    UseWaitCursor=false

    Windows.Forms.Cursor.Position = Windows.Forms.Cursor.Position

     

    This forces the refresh, and you can use UseWaitCursor without changing the actual cursor of the controls...

     

    Thursday, May 20, 2010 7:15 AM
  • I got it to work as expected by calling the function 3 times in a row:

    {

    System.Windows.Forms.Application.UseWaitCursor = true;
    System.Windows.Forms.Application.UseWaitCursor = false;
    System.Windows.Forms.Application.UseWaitCursor = true;

    // DO SOME WORK

    System.Windows.Forms.Application.UseWaitCursor = false;
    System.Windows.Forms.Application.UseWaitCursor = true;
    System.Windows.Forms.Application.UseWaitCursor = false;

    }

    Wednesday, June 09, 2010 12:50 AM
  • Or:

    Try

      Application.UseWaitCursor = True

      Application.DoEvents()

     

      ' Count sheep

    Catch ex as Exception

    Finally

      Application.UseWaitCursor = False

    End Try

     

     

    Works for me.

    • Proposed as answer by Cor LigthertMVP Wednesday, February 05, 2014 9:58 AM
    Tuesday, February 22, 2011 8:06 AM
  • I'm using the following methods to toggle the wait cursor in a VB.NET Class Module that exports data to Excel:

    'Display hourglass during export
    Cursor.Current = Cursors.WaitCursor
    Cursor.Show()
    
    'Export datagridview data to Excel...
    
    'Hide hourglass after export
    Cursor.Current = Cursors.Default
    Cursor.Show()
    

    Calling Cursor.Show() after updating the cursor type always seems to refresh the cursor as expected and works really well. When you move the cursor over other controls, the wait cursor remains active until the code has finished executing.

    :-)

    • Proposed as answer by PHBeagle Tuesday, February 18, 2014 4:09 PM
    Thursday, May 19, 2011 10:32 AM
  • Challenge: set a wait cursor on all forms in an Application. Application.UseWaitCursor doesn't seem effective as discussed previously. I wanted:

    1. a simple method
    2. to employ the using{} construct as the cleanest way to define the start and end of the wait cursor
    3. to avoid unmanaged code.

    This is my solution, first in C# and then in VB.


    In C#:

    using System;
    using System.Windows.Forms;
    
    
        class WaitCursor : IDisposable
        {
            public WaitCursor()
            {
                this.SetCursorAllForms(Cursors.WaitCursor);
            }
    
            public void SetCursorAllForms(Cursor cursor)
            {
                foreach (Form frm in Application.OpenForms)
                {
                    frm.Cursor = cursor;
                }
    
            }
    
            public void Dispose()
            {
                // Class must implement Idisposable.Dispose() so that
                // Using construct may be employed
                this.SetCursorAllForms(Cursors.Default);
            }
    
        }
    
    
    

    To deploy this class:

    Create a new class and paste the above code in. Then when you need a wait cursor, insert the following:

    using (WaitCursor WC = new WaitCursor())
    {
        // do your long operation
        // optionally you can change the cursor at any time with e.g.:
        WC.SetCursorAllForms(Cursors.Help);
        // complete your long operation            
    }
    



    In VB:

     

    Imports System
    
    Public Class WaitCursor
        Implements IDisposable
    
        Public Sub New()
            Me.SetCursorAllForms(Cursors.WaitCursor)
        End Sub
    
        Public Sub SetCursorAllForms(ByVal cursor As Cursor)
            Dim frm As Form
            For Each frm In Application.OpenForms
                frm.Cursor = cursor
            Next
        End Sub
    
        Public Sub Dispose() Implements IDisposable.Dispose
            ' Class must implement Idisposable.Dispose() so that
            ' Using construct may be employed
            Me.SetCursorAllForms(Cursors.Default)
        End Sub
    
    End Class
    

    To deploy this class:
    Create a new class and paste the above code in. Then when you need a wait cursor, insert the following:

    Using WC As New WaitCursor
        ' do your long operation
        ' optionally you can change the cursor at any time with e.g.:
        WC.SetCursorAllForms(Cursors.Help)
        ' complete your long operation
    End Using
    

     

     

    Monday, October 17, 2011 11:56 AM
  • DoEvents() was the trick for me.  Many thanks!
    Tuesday, February 04, 2014 11:55 PM