Click here to Skip to main content
15,886,100 members
Articles / Programming Languages / C#
Tip/Trick

Hourglass Mouse Cursor Always Changes Back to its Original Image. How?

Rate me:
Please Sign up or sign in to vote.
4.85/5 (11 votes)
21 Dec 2010CPOL1 min read 28.3K   10   9
IDisposable with “using” block helps in all cases
Testing and debugging UI code, I am often faced with such a situation, when the mouse cursor is changed to Hourglass, but not changed back.
Sometimes, it happened to my own code as well.

The mouse cursor is supposed to be changed back to its original or default image after the procedure which could cause a delay if UI response is already completed (or failed). Sometimes it is not changed back because this procedure fails due to exception or some other reason. Sometimes the code used to set mouse cursor to its original image is simply forgotten.

As a result, I came to a simple pattern which helps to avoid such situation in all cases. I used it in several different UI systems. Now I'll show how it is implemented with .NET. The idea is using helper class implementing IDisposable.

This is a WPF implementation written in C#:

C#
namespace SA.Universal.UI {
    using IDisposable = System.IDisposable;
    using FrameworkElement = System.Windows.FrameworkElement;
    using Cursor = System.Windows.Input.Cursor;
    using Cursors = System.Windows.Input.Cursors;
    using Debug = System.Diagnostics.Debug;

    public class WaitCursorIndicator : IDisposable {

        public WaitCursorIndicator(FrameworkElement owner) {
            this.Onwer = owner;
            Debug.Assert(
                owner != null,
                "WaitCursorIndicator expects non-null argument");
            if (owner == null) return;
            Previous = owner.Cursor;
            owner.Cursor = Cursors.Wait;
        } //WaitCursorIndicator

        void IDisposable.Dispose() {
            if (this.Onwer == null) return;
            this.Onwer.Cursor = Previous;
        } //IDisposable.Dispose

        FrameworkElement Onwer;
        Cursor Previous;

    } //class WaitCursorIndicator

} //namespace SA.Universal.UI


With System.Windows.Forms, it is implemented in a very similar way:

C#
namespace SA.Universal.UI {
    using System.Windows.Forms;
    using IDisposable = System.IDisposable;
    using Debug = System.Diagnostics.Debug;

    public class WaitCursorIndicator : System.IDisposable {

        public WaitCursorIndicator(Control owner) {
            this.Owner = owner;
            Debug.Assert(
                owner != null,
                "WaitCursorIndicator expects non-null argument");
            if (owner == null) return;
            Previous = this.Owner.Cursor;
            owner.Cursor = Cursors.WaitCursor;
        } //WaitCursorIndicator

        void IDisposable.Dispose() {
            if (Owner == null) return;
            Owner.Cursor = Previous;
        } //IDisposable.Dispose

        Control Owner;
        Cursor Previous;

    } //class WaitCursorIndicator

} //namespace SA.Universal.UI


The usage looks identical for WPF and System.Windows.Forms:

C#
using(new WaitCursorIndicator(owner)) {
   //... some long-running code here
} //end using


In this code snippet, owner is most typically a form or a window.

This way, mouse cursor is changed back no matter what, even if an exception is thrown.
On exit from the "using" block, WaitCursorIndicator.Dispose is always called. In fact, using this "using" block is just syntactic sugar functionally strictly equivalent to the try-finally block with Dispose called in the finally section.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect
United States United States
Physics, physical and quantum optics, mathematics, computer science, control systems for manufacturing, diagnostics, testing, and research, theory of music, musical instruments… Contact me: https://www.SAKryukov.org

Comments and Discussions

 
GeneralMessage Removed Pin
19-Dec-10 7:05
mvaSergey Alexandrovich Kryukov19-Dec-10 7:05 
GeneralYou're right, of course, but in real life people make mistak... Pin
Sergey Alexandrovich Kryukov19-Dec-10 7:00
mvaSergey Alexandrovich Kryukov19-Dec-10 7:00 
You're right, of course, but in real life people make mistakes. If you did not make them, other people did; I know for sure. Also, are you sure try-finally block was always applied, or you simply did not encounter the exception?

Now, "must ensure the mouse pointer is in the correct state" means the procedure which is repeated over and over, so that is a lack of code re-use and a source of mistake. Also, this "must ensure" is interlacing different unrelated concerns in the same code, always not good (read about "separation of concerns").

Better technique is not the one relying on memory and attention of the developer, but the one which helps the developer to forget boring detail and concentrate on semantics.
In this case, same thing: "if a code changed the mouse pointer, then the code must change it back", but this code is re-used (completely done by the brackets of the "using" block) and separated from "semantic" code provided in the middle by the user of the technique. Much better, isn't it?
GeneralI always thought that if a code changed the mouse pointer, t... Pin
DaveAuld18-Dec-10 21:25
professionalDaveAuld18-Dec-10 21:25 
GeneralRe: I always thought that if a code changed the mouse pointer, t... Pin
NGErndt10-Jul-20 14:13
NGErndt10-Jul-20 14:13 
QuestionRe: I always thought that if a code changed the mouse pointer, t... Pin
Sergey Alexandrovich Kryukov30-Jan-21 10:00
mvaSergey Alexandrovich Kryukov30-Jan-21 10:00 
AnswerRe: I always thought that if a code changed the mouse pointer, t... Pin
DaveAuld30-Jan-21 21:16
professionalDaveAuld30-Jan-21 21:16 
AnswerIt was just the relocation of my misplaced post Pin
Sergey Alexandrovich Kryukov31-Jan-21 3:58
mvaSergey Alexandrovich Kryukov31-Jan-21 3:58 
AnswerYour Astrophotography channel Pin
Sergey Alexandrovich Kryukov31-Jan-21 4:09
mvaSergey Alexandrovich Kryukov31-Jan-21 4:09 
GeneralRe: Your Astrophotography channel Pin
DaveAuld31-Jan-21 4:19
professionalDaveAuld31-Jan-21 4:19 
AnswerRe: Your Astrophotography channel Pin
Sergey Alexandrovich Kryukov31-Jan-21 9:25
mvaSergey Alexandrovich Kryukov31-Jan-21 9:25 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.