Я заметил интересную вещь об установке курсоров, поэтому я хотел бы устранить некоторые недоразумения, которые у меня были раньше, и я надеюсь, что это может помочь и другим:
При попытке установить курсор формы с помощью
this.cursor = Cursors.Waitcursor
вы фактически устанавливаете курсор для элемента управления, а не всю форму, поскольку курсор является свойством класса Control.
Также, конечно, курсор будет изменен на данный курсор только тогда, когда мышь фактически находится над фактическим элементом управления (явно область формы)
Как уже сказал Ганс Пассант:
Windows отправляет окно, содержащее курсор мыши,
Сообщение WM_SETCURSOR, дающее возможность изменить курсор
форма
Я не знаю, отправляет ли Windows сообщения непосредственно элементам управления или же форма передает эти сообщения своим дочерним элементам управления в зависимости от положения мыши, я, скорее всего, догадываюсь о первом методе с тех пор, как я извлек сообщения с переопределением WndProc элемента управления формой, например, когда я находился над текстовым полем, форма не обрабатывала никаких сообщений. (пожалуйста, кто-нибудь прояснит это)
По сути, мое предложение было бы отказаться от использования this.cursor и придерживаться this.usewaitcursor, поскольку это изменяет свойство курсора на waitcursor для всех дочерних элементов управления.
Проблема с этим также та же, что и с уровнем приложения Application.usewaitcursor, пока вы не перешли через формы / формы с помощью курсора, никакое сообщение WM_SETCURSOR не отправляется окнами, поэтому, если вы запускаете синхронную операцию, отнимающую много времени перед тем, как навести курсор мыши на область формы, форма может обработать такое сообщение только после завершения трудоемкой синхронной операции.
(я бы вообще не советовал запускать трудоемкие задачи в потоке пользовательского интерфейса, в основном это является причиной проблемы)
Я немного улучшил ответ Ханса Пассанта, так что песочные часы можно установить либо на уровне приложения, либо на уровне формы, также избегая исключения InvalidOperationException от вызовов многопоточных операций:
using System;
using System.Windows.Forms;
public class HourGlass : IDisposable
{
public static bool ApplicationEnabled
{
get{ return Application.UseWaitCursor; }
set
{
Form activeFrom = Form.ActiveForm;
if (activeFrom == null || ApplicationEnabled == value) return;
if (ApplicationEnabled == value)return;
Application.UseWaitCursor = (bool)value;
if (activeFrom.InvokeRequired)
{
activeFrom.BeginInvoke(new Action(() =>
{
if (activeFrom.Handle != IntPtr.Zero)
SendMessage(activeFrom.Handle, 0x20, activeFrom.Handle, (IntPtr)1); // Send WM_SETCURSOR
}));
}
else
{
if (activeFrom.Handle != IntPtr.Zero)
SendMessage(activeFrom.Handle, 0x20, activeFrom.Handle, (IntPtr)1); // Send WM_SETCURSOR
}
}
}
private Form f;
public HourGlass()
{
this.f = Form.ActiveForm;
if (f == null)
{
throw new ArgumentException();
}
Enabled = true;
}
public HourGlass(bool enabled)
{
this.f = Form.ActiveForm;
if (f == null)
{
throw new ArgumentException();
}
Enabled = enabled;
}
public HourGlass(Form f, bool enabled)
{
this.f = f;
if (f == null)
{
throw new ArgumentException();
}
Enabled = enabled;
}
public HourGlass(Form f)
{
this.f = f;
if (f == null)
{
throw new ArgumentException();
}
Enabled = true;
}
public void Dispose()
{
Enabled = false;
}
public bool Enabled
{
get { return f.UseWaitCursor; }
set
{
if (f == null || Enabled == value) return;
if (Application.UseWaitCursor == true && value == false) return;
f.UseWaitCursor = (bool)value;
if(f.InvokeRequired)
{
f.BeginInvoke(new Action(()=>
{
if (f.Handle != IntPtr.Zero)
SendMessage(f.Handle, 0x20, f.Handle, (IntPtr)1); // Send WM_SETCURSOR
}));
}
else
{
if (f.Handle != IntPtr.Zero)
SendMessage(f.Handle, 0x20, f.Handle, (IntPtr)1); // Send WM_SETCURSOR
}
}
}
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
}
Чтобы использовать его на уровне приложения:
try
{
HourGlass.ApplicationEnabled = true;
//time consuming synchronous task
}
finally
{
HourGlass.ApplicationEnabled = false;
}
Для использования на уровне формы вы можете использовать для текущей активной формы:
using (new HourGlass())
{
//time consuming synchronous task
}
или вы можете инициализировать локальную переменную в виде:
public readonly HourGlass hourglass;
public Form1()
{
InitializeComponent();
hourglass = new HourGlass(this, false);
}
и использовать его позже в блоке try catch наконец