Cheshire Cat
Cheshire Cat

Reputation: 1961

Cursor not updating with WaitCursor using TPL Task

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;
}

Upvotes: 1

Views: 467

Answers (1)

Cheshire Cat
Cheshire Cat

Reputation: 1961

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...
}

Upvotes: 1

Related Questions