Reputation: 1961
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
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