Я использую пользовательский класс (WaitCursor
), чтобы гарантировать, что каждый раз, когда я запускаю длительную задачу, Cursor
обновляется, чтобы показать WaitCursor
. Оригинальную статью и код можно найти здесь. Внедрение класса WaitCursor. SendMessage()
и GetForegroundWindow()
были взяты из другого ответа здесь, в StackOverflow.
Я успешно использовал этот класс, когда использовал класс Thread
для выполнения методов в отдельных потоках. Курсор был обновлен во время выполнения, а затем вернулся к умолчанию.
Затем я изменил свою логику, чтобы использовать новый класс Task
из библиотеки TPL вместо 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;
}
Похоже, что проблема была вызвана методом DisableForm
. Как-то отключение формы останавливает процесс обновления курсора.
Так что я, наконец, нашел решение, заменив DisableForm
метод с DisableControls
.
private void DisableControls()
{
foreach (Control control in Controls)
{
control.Enabled = false;
}
}
Остальная часть кода, поэтому класс WaitCursor
и использование класса остаются неизменными:
using (new WaitCursor())
{
// Do something that takes long time...
}
Task
/Thread
.