132

On several of my usercontrols, I change the cursor by using

this.Cursor = Cursors.Wait;

when I click on something.

Now I want to do the same thing on a WPF page on a button click. When I hover over my button, the cursor changes to a hand, but when I click it, it doesn't change to the wait cursor. I wonder if this has something to do with the fact that it's a button, or because this is a page and not a usercontrol? This seems like weird behavior.

ABCD
  • 897
  • 16
  • 38
ScottG
  • 10,711
  • 25
  • 82
  • 111

5 Answers5

224

Do you need the cursor to be a "wait" cursor only when it's over that particular page/usercontrol? If not, I'd suggest using Mouse.OverrideCursor:

Mouse.OverrideCursor = Cursors.Wait;
try
{
    // do stuff
}
finally
{
    Mouse.OverrideCursor = null;
}

This overrides the cursor for your application rather than just for a part of its UI, so the problem you're describing goes away.

Matt Hamilton
  • 200,371
  • 61
  • 386
  • 320
  • Similar to my own [answer](http://stackoverflow.com/a/8211178/448232), dated 3 years later(almost exactly!). I like the answers in this question, but the simplest one is always the most tempting :) – Robin Maben Jul 31 '12 at 13:58
  • This solution will change the cursor to be a "wait" cursor but it will not disable any further mouse inputs. I tried using this solution and although the mouse changed to the wait cursor I am still able to click any UI element within my WPF application without any problem. Any ideas how I can prevent the user from actually using the mouse during the wait cursor is active? – Thomas Huber Dec 04 '12 at 14:46
  • 3
    Old as it is and accepted as it is, it's NOT the proper answer. Overriding the app cursor is different than overriding a control cursor (and the second has problems in WPF all right). Overriding the app cursor can have nasty side effects, for instance, a popping up (error) message box might be forced to use the same overridden cursor erroneously while the intention was only to override while the mouse is hovering over the actual and active control. – Gábor Jun 06 '14 at 18:48
66

One way we do this in our application is using IDisposable and then with using(){} blocks to ensure the cursor is reset when done.

public class OverrideCursor : IDisposable
{

  public OverrideCursor(Cursor changeToCursor)
  {
    Mouse.OverrideCursor = changeToCursor;
  }

  #region IDisposable Members

  public void Dispose()
  {
    Mouse.OverrideCursor = null;
  }

  #endregion
}

and then in your code:

using (OverrideCursor cursor = new OverrideCursor(Cursors.Wait))
{
  // Do work...
}

The override will end when either: the end of the using statement is reached or; if an exception is thrown and control leaves the statement block before the end of the statement.

Update

To prevent the cursor flickering you can do:

public class OverrideCursor : IDisposable
{
  static Stack<Cursor> s_Stack = new Stack<Cursor>();

  public OverrideCursor(Cursor changeToCursor)
  {
    s_Stack.Push(changeToCursor);

    if (Mouse.OverrideCursor != changeToCursor)
      Mouse.OverrideCursor = changeToCursor;
  }

  public void Dispose()
  {
    s_Stack.Pop();

    Cursor cursor = s_Stack.Count > 0 ? s_Stack.Peek() : null;

    if (cursor != Mouse.OverrideCursor)
      Mouse.OverrideCursor = cursor;
  }

}
Dennis
  • 20,275
  • 4
  • 64
  • 80
  • Thanks John. It is very useful when you start to nest the cursor changes when there are multiple entry points. Also note the checks to prevent the cursor from flickering. It equates to basically the same IL as the selected answer. – Dennis Sep 10 '09 at 10:53
  • 2
    Nice solution with the using part. I actually wrote exactly the same in some of our projects (without the stack, that is). One thing you can simplify in the usage is to just write: using (new OverrideCursor(Cursors.Wait)) { //do stuff } instead of assigning it a variable you probably will not use. – Olli May 18 '10 at 14:25
  • i think you need a not null check, or am not sure what happens if you set null for cursor! lemme check! Cursor cursor = s_Stack.Count > 0 ? s_Stack.Peek() : null; if (cursor!=null) && (cursor != Mouse.OverrideCursor) Mouse.OverrideCursor = cursor; – ioWint Aug 11 '11 at 01:12
  • 1
    Not needed. If you set `Mouse.OverrideCursor` to `null` it is unset and no longer overrides the system cursor. *IF* I was modifying the current cursor directly (i.e. not overriding) then there could be a problem. – Dennis Aug 11 '11 at 09:32
  • back to you, Dennis :) i jus implemented the stack approach and it jus caught me where a button click with wait cursor in one of my View, conditionally opens another as a Modal. The modal window has Service logic implemented in the load. i need a Wait cursor on load So the stack is actually troubling me in this case! the modal loaded has the cursor as Wait Cursor!! i think i should go with out a stack, accepting the flicker! – ioWint Aug 17 '11 at 23:00
  • 2
    This is nice, but is not safe if multiple views are updating the cursor at the same time. Easy to get into a race condition where ViewA set's cursor, then ViewB sets a different one, then ViewA tries to reset its cursor (which then pops ViewB's off the stack and leaves ViewA's cursor active). Not until ViewB resets its cursor do things get back to normal. – Simon Gillbee Jun 06 '18 at 21:00
  • 2
    @SimonGillbee that is indeed possible - it wasn't a problem that I had 10yrs ago when i wrote this. if you find a solution, perhaps using a `ConcurrentStack`, feel free to edit the above answer or add your own. – Dennis Jun 08 '18 at 02:27
  • 2
    @Dennis I actually wrote this a few days ago (which is why I was looking thru SO). I played with ConcurrentStack, but it turned out to be the wrong collection. Stack only allows you to pop off the top. In this case you want to remove from the middle of the stack if that cursor is disposed before the top of the stack is disposed. I ended up just using List with ReaderWriterLockSlim to govern concurrent access. – Simon Gillbee Jun 09 '18 at 04:46
43

You can use a data trigger (with a view model) on the button to enable a wait cursor.

<Button x:Name="NextButton"
        Content="Go"
        Command="{Binding GoCommand }">
    <Button.Style>
         <Style TargetType="{x:Type Button}">
             <Setter Property="Cursor" Value="Arrow"/>
             <Style.Triggers>
                 <DataTrigger Binding="{Binding Path=IsWorking}" Value="True">
                     <Setter Property="Cursor" Value="Wait"/>
                 </DataTrigger>
             </Style.Triggers>
         </Style>
    </Button.Style>
</Button>

Here is the code from the view-model:

public class MainViewModel : ViewModelBase
{
   // most code removed for this example

   public MainViewModel()
   {
      GoCommand = new DelegateCommand<object>(OnGoCommand, CanGoCommand);
   }

   // flag used by data binding trigger
   private bool _isWorking = false;
   public bool IsWorking
   {
      get { return _isWorking; }
      set
      {
         _isWorking = value;
         OnPropertyChanged("IsWorking");
      }
   }

   // button click event gets processed here
   public ICommand GoCommand { get; private set; }
   private void OnGoCommand(object obj)
   {
      if ( _selectedCustomer != null )
      {
         // wait cursor ON
         IsWorking = true;
         _ds = OrdersManager.LoadToDataSet(_selectedCustomer.ID);
         OnPropertyChanged("GridData");

         // wait cursor off
         IsWorking = false;
      }
   }
}
Zamboni
  • 7,897
  • 5
  • 43
  • 52
  • 4
    I don't get the downvote either. This answer is useful when you're using MVvM (so no code-behind) and want to control the cursor for a specific control. Very useful. – Simon Gillbee Sep 10 '10 at 16:07
  • 5
    I'm leveraging the benefits of MVVM and this is the perfect answer. – g1ga Jul 10 '12 at 22:20
  • I like this solution as I believe it will work better with MVVM, viewmodels, etc. – Rod Feb 17 '16 at 21:43
  • The problem I see with this code is that the cursors is "Wait" only while the mouse is overing over the button, but as you move the mouse out it returns to be an "Arrow". – spiderman May 24 '18 at 14:51
7

If your application uses async stuff and you're fiddling with Mouse's cursor, you probably want to do it only in main UI thread. You can use app's Dispatcher thread for that:

Application.Current.Dispatcher.Invoke(() =>
{
    // The check is required to prevent cursor flickering
    if (Mouse.OverrideCursor != cursor)
        Mouse.OverrideCursor = cursor;
});
0

The following worked for me:

ForceCursor = true;
Cursor = Cursors.Wait;
Mike Lowery
  • 2,630
  • 4
  • 34
  • 44