使用TPL任务不使用WaitCursor更新游标

时间:2014-12-09 16:05:48

标签: c# multithreading cursor task-parallel-library task

我正在使用自定义类(WaitCursor)来确保每次运行长时间运行的任务时,Cursor都会更新以显示WaitCursor。可以在Implementing a WaitCursor Class找到原始文章和代码。 SendMessage()GetForegroundWindow()来自 StackOverflow 的另一个答案here

当我使用Thread类在不同的线程上执行方法时,我成功地使用了这个类。游标在执行期间更新,然后恢复为默认值。

然后我更改了逻辑以使用 TPL库中的新Task类而不是Thread,现在光标不再更新,我总是看到默认值光标。

代码中没有其他更改。在使用Task时,每次调用我的类创建新Thread的方法都与以前相同。

以下是我的代码。

这是TaskManager,我用来管理Task执行的类。 ThreadManager是前一个具有完全相同逻辑的类。唯一的区别在于调用创建新Thread / Task的方法,因此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;
    }
}

致电创建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上课

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实施

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

我唯一更改的是Task而不是Thread,它对我的​​方法和EventHandler也是透明的。

Thread和TPL Task之间管理游标更新有何不同?

更新1

正如@JimMischel建议的那样,我尝试使用Invoke方法而不是UseWaitCursor类,但它不起作用。这是代码。

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

1 个答案:

答案 0 :(得分:1)

问题似乎是branch(foo)方法造成的。以某种方式禁用表单会停止游标更新过程

所以我终于找到了一个解决方案DisableForm取代 DisableForm方法。

DisableControls

代码的其余部分,所以private void DisableControls() { foreach (Control control in Controls) { control.Enabled = false; } } 类和类的用法保持不变:

WaitCursor