1

I'm using a custom class (WaitCursor) to ensure that everytime I run a long running task, the Cursor is updated to show the WaitCursor. The original article and code can be found here Implementing a WaitCursor Class. The SendMessage() and GetForegroundWindow() was taken from another answer here at StackOverflow.

I was succesfully using this class when I used the Thread class to execute methods on separate threads. The cursor was updated during the execution and then reverted back to default.

Then I changed my logic to use the new Task class from TPL Library instead of Thread and now the cursor doesn't update anymore, I always see the default cursor.

No other changes where made in the code. Each call to the method of my class that create a new Task, is the same as before, when using Thread.

Here follows my code.

This is TaskManager, the class I use to manage execution of Task. ThreadManager was the previous class that shares exactly the same logic. The only difference is in the call to the method that create the new Thread/Task, so Thread.Start()/Task.Factory.StartNew().

public class TaskManager
{
    private static readonly TaskManager Instance = new TaskManager();
    private readonly Dictionary<int, Task> _tasksList;
    private static int _tasksCount;

    private TaskManager()
    {
        _tasksList = new Dictionary<int, Task>();
        _tasksCount = 0;
    }

    public static TaskManager GetInstance()
    {
        return Instance;
    }

    public int StartNewTask(Action method)
    {
        try
        {
            Task task = Task.Factory.StartNew(method);

            _tasksCount++;
            _tasksList.Add(task.Id, task);

            return task.Id;
        }
        catch (Exception ex)
        {
            // Manage exception and log error
        }

        return -1;
    }
}

Call to create Thread/Task

private void btnOK_Click(object sender, EventArgs e)
{
    // Before, using Thread class
    _threadManager.StartNewThread(MyMethod);

    // Now, using Task class
    _taskManager.StartNewTask(MyMethod);
}

WaitCursor class

public class WaitCursor : IDisposable
{
    public WaitCursor()
    {
        Enabled = true;
    }

    public void Dispose()
    {
        Enabled = false;
    }

    public static bool Enabled
    {
        get
        {
            return Application.UseWaitCursor;
        }

        set
        {
            if (value == Application.UseWaitCursor) return;
            Application.UseWaitCursor = value;
            Cursor.Current = value ? Cursors.WaitCursor : Cursors.Default;
            var handle = GetForegroundWindow();
            SendMessage(handle, 0x20, handle, (IntPtr)1); // Send WM_SETCURSOR
            Cursor.Position = Cursor.Position; // Trick to update the cursor even if the user doesn't move the mouse
        }
    }

    [System.Runtime.InteropServices.DllImport("user32.dll")]
    private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);

    [System.Runtime.InteropServices.DllImport("user32.dll")]
    private static extern IntPtr GetForegroundWindow();
}

MyMethod implementation

private void MyMethod()
{
    using (new WaitCursor())
    {
        // Do something that takes long time...
    }
}

The only thing I changed is Task instead of Thread and it's transparent to my method and to the EventHandler too.

What's the difference in managing cursor's updates between Thread and TPL Task?

UPDATE 1

As @JimMischel suggested, I tried to use the Invoke method instead of the UseWaitCursor class, but it doesn't work. This is the code.

private void btnLogin_Click(object sender, EventArgs e)
{
    // Start a new Task for MyMethod
    _taskManager.StartNewTask(MyMethod);
}

private void MyMethod()
{
    Invoke((MethodInvoker) DisableForm);
    Invoke((MethodInvoker) ToggleWaitCursor);

    // Do something that takes long time...

    Invoke((MethodInvoker) EnableForm);
    Invoke((MethodInvoker) ToggleWaitCursor);
}

private void ToggleWaitCursor()
{
    if (this.UseWaitCursor)
        this.UseWaitCursor = false;
    else
        this.UseWaitCursor = true;
}

private void DisableForm()
{
    this.Enabled = false;
}

private void EnableForm()
{
    this.Enabled = true;
}
Community
  • 1
  • 1
Cheshire Cat
  • 1,941
  • 6
  • 36
  • 69
  • You need to *show us the code* if you want us to have any idea what's wrong with it. You only showed the code that calls the code that does this. – Servy Dec 09 '14 at 16:07
  • I updated the code and added the class I use to manage `Task`/`Thread`. – Cheshire Cat Dec 09 '14 at 16:15
  • Seems like the line `Cursor.Position = Cursor.Position` is somewhat dangerous to be executing in a non-UI thread. I'm surprised that it doesn't throw an exception. One difference is that `Thread` by default creates a foreground thread. A `Task` is executed on a pool thread, which is a background thread. I wouldn't really expect that to matter here, though. Come to think of it, couldn't you just do an `Invoke` to set the cursor, and get rid of the complicated `WaitCursor` logic? – Jim Mischel Dec 09 '14 at 16:34
  • 1
    Proper attribution when you copy somebody's code is required at SO. – Hans Passant Dec 09 '14 at 17:36
  • @HansPassant Yes, you're right! I forgot it! That was the article: [Implementing a WaitCursor Class](http://www.blackbeltcoder.com/Articles/winforms/implementing-a-waitcursor-class). I also added it on the original question. – Cheshire Cat Dec 10 '14 at 08:32
  • @JimMischel First I tried with a simpler approach, such as `Cursor.Current = Cursors.WaitCursor` etc... and then also with `Application.DoEvents()` but it was not working. Then I found this class that was working very good and I think it is also readable code and easy to use. I can try to remove the `Cursor.Position = Cursor.Position`, but as you already said, I don't think that would solve the problem. Anyway, that was a trick to force cursor update even when the user doesn't move the mouse. But, my real question is why that code works great with `Thread` but not with `Task`? – Cheshire Cat Dec 10 '14 at 08:41
  • @JimMischel Removing `Cursor.Position = Cursor.Position` leads to a different behaviour! The cursor updates... but, when I move the mouse it reverts back to the arrow! – Cheshire Cat Dec 10 '14 at 09:39
  • I realize the removing the code would cause different behavior. I'm just surprised it works. That notwithstanding, I'm still trying to understand why you don't just call [Form.Invoke](http://msdn.microsoft.com/en-us/library/zyzhdc6b(v=vs.110).aspx) to set the cursor rather than jumping through all the hoops required by that `WaitCursor` code. Seems like if you Invoked a call to set `Application.WaitCursor`, that would be executed on the UI thread, which *should* cause the UI to update. As to why your code works as a thread but not a task: no clue. – Jim Mischel Dec 10 '14 at 12:31
  • Because I'd like to separate Task calculation and UI update. So the `using (new WaitCursor()){...}` can do the trick. Did you read the article [here](http://www.blackbeltcoder.com/Articles/winforms/implementing-a-waitcursor-class)? There's a complete explanation of possible problems that this class can handle in a simpler way. The other possibility is to insert everything inside a `try-catch-finally` block where inside the `try` block the cursor is set and then in case of exceptions, the cursor is resotred inside the `finally` block. But this code needs to be replicated for each method I call. – Cheshire Cat Dec 10 '14 at 13:22
  • I read the article. And it's a useful thing if you want to do long calculations on the UI thread, as he demonstrates. But the article is fatally flawed specifically because he's performing long-running calculations on the UI thread. You, on the other hand, are doing the work in a background thread. So you shouldn't have the same problem. You could set `UseWaitCursor` before spawning the task. Or you could have the task invoke (or `BeginInvoke`) that setting. But if you want to overcomplicate your code with unnecessary goofiness, go for it. – Jim Mischel Dec 10 '14 at 15:09
  • I tried but it doesn't work. Take a look at the updated question... – Cheshire Cat Dec 11 '14 at 10:21
  • That's pretty odd. Does the updated version (using Invoke) work when you run it as a thread instead of as a task? – Jim Mischel Dec 11 '14 at 23:00
  • Yes, just tried but not working too. – Cheshire Cat Dec 15 '14 at 11:26

1 Answers1

1

It seems that the problem was due to the DisableForm method. Somehow disabling the Form stops the cursor updating process.

So I finally found a solution by replacing the DisableForm method with DisableControls.

private void DisableControls()
{
    foreach (Control control in Controls)
    {
        control.Enabled = false;
    }
}

The rest of the code, so the WaitCursor class and the usage of the class, remains the same:

using (new WaitCursor())
{
    // Do something that takes long time...
}
Cheshire Cat
  • 1,941
  • 6
  • 36
  • 69