如何使光标转到等待光标?

我如何向用户显示等待/忙碌游标(通常是沙漏),让他们知道程序正在做什么?

497666 次浏览

你可以使用Cursor.Current

// Set cursor as hourglass
Cursor.Current = Cursors.WaitCursor;


// Execute your time-intensive hashing code here...


// Set cursor as default arrow
Cursor.Current = Cursors.Default;

然而,如果哈希操作是真的长(MSDN定义为超过2-7秒),你可能应该使用一个视觉反馈指示器而不是游标来通知用户进度。要了解更深入的指导原则,请参见这篇文章

< p > 编辑: < br > 正如@Am指出的那样,你可能需要在Cursor.Current = Cursors.WaitCursor;之后调用Application.DoEvents();来确保沙漏确实显示出来。< / p >

我的方法是在后台工作程序中进行所有的计算。

然后像这样改变光标:

this.Cursor = Cursors.Wait;

并且在线程的finish事件中恢复游标:

this.Cursor = Cursors.Default;

注意,这也可以用于特定的控件,所以只有当鼠标在沙漏上方时,光标才会是沙漏。

实际上,

Cursor.Current = Cursors.WaitCursor;

暂时设置等待游标,但不确保等待游标直到操作结束才显示。程序中的其他程序或控件可以很容易地将光标重置回默认箭头,就像在操作仍在运行时移动鼠标一样。

一个更好的显示等待游标的方法是在表单中设置UseWaitCursor属性为true:

form.UseWaitCursor = true;
这将显示窗体上所有控件的等待游标,直到将此属性设置为false。 如果你想要等待光标显示在应用程序级别,你应该使用:

Application.UseWaitCursor = true;
在窗体或窗口级别使用UseWaitCursor更容易。 一个典型的用例如下所示
    private void button1_Click(object sender, EventArgs e)
{


try
{
this.Enabled = false;//optional, better target a panel or specific controls
this.UseWaitCursor = true;//from the Form/Window instance
Application.DoEvents();//messages pumped to update controls
//execute a lengthy blocking operation here,
//bla bla ....
}
finally
{
this.Enabled = true;//optional
this.UseWaitCursor = false;
}
}

为了获得更好的UI体验,你应该在不同的线程中使用异步。

在前面的基础上,我的首选方法(因为这是一个经常执行的操作)是将等待游标代码包装在一个IDisposable助手类中,这样它就可以与using()(一行代码)一起使用,接受可选参数,在其中运行代码,然后清理(恢复游标)。

public class CursorWait : IDisposable
{
public CursorWait(bool appStarting = false, bool applicationCursor = false)
{
// Wait
Cursor.Current = appStarting ? Cursors.AppStarting : Cursors.WaitCursor;
if (applicationCursor) Application.UseWaitCursor = true;
}


public void Dispose()
{
// Reset
Cursor.Current = Cursors.Default;
Application.UseWaitCursor = false;
}
}

用法:

using (new CursorWait())
{
// Perform some code that shows cursor
}

我创建了一个静态异步方法。这将禁用启动操作并更改应用程序游标的控件。它将操作作为任务运行,并等待完成。控件在等待时返回给调用者。因此,即使在忙碌的图标旋转时,应用程序也能保持响应。

async public static void LengthyOperation(Control control, Action action)
{
try
{
control.Enabled = false;
Application.UseWaitCursor = true;
Task doWork = new Task(() => action(), TaskCreationOptions.LongRunning);
Log.Info("Task Start");
doWork.Start();
Log.Info("Before Await");
await doWork;
Log.Info("After await");
}
finally
{
Log.Info("Finally");
Application.UseWaitCursor = false;
control.Enabled = true;
}

这是主窗体的代码

    private void btnSleep_Click(object sender, EventArgs e)
{
var control = sender as Control;
if (control != null)
{
Log.Info("Launching lengthy operation...");
CursorWait.LengthyOperation(control, () => DummyAction());
Log.Info("...Lengthy operation launched.");
}


}


private void DummyAction()
{
try
{
var _log = NLog.LogManager.GetLogger("TmpLogger");
_log.Info("Action - Sleep");
TimeSpan sleep = new TimeSpan(0, 0, 16);
Thread.Sleep(sleep);
_log.Info("Action - Wakeup");
}
finally
{
}
}

我不得不使用一个单独的日志记录器的虚拟动作(我使用Nlog)和我的主日志记录器写入用户界面(一个富文本框)。只有在表单上的特定容器上才能显示繁忙的游标(但我没有很努力地尝试)。所有控件都有一个UseWaitCursor属性,但它似乎对我尝试的控件没有任何影响(可能是因为它们不在顶部?)

这是主日志,它显示了事情按照我们预期的顺序发生:

16:51:33.1064 Launching lengthy operation...
16:51:33.1215 Task Start
16:51:33.1215 Before Await
16:51:33.1215 ...Lengthy operation launched.
16:51:49.1276 After await
16:51:49.1537 Finally

使用下面的类,您可以使Donut的建议“异常安全”。

using (new CursorHandler())
{
// Execute your time-intensive hashing code here...
}

类CursorHandler

public class CursorHandler
: IDisposable
{
public CursorHandler(Cursor cursor = null)
{
_saved = Cursor.Current;
Cursor.Current = cursor ?? Cursors.WaitCursor;
}


public void Dispose()
{
if (_saved != null)
{
Cursor.Current = _saved;
_saved = null;
}
}


private Cursor _saved;
}

好的,其他人的观点很清楚,但我想补充一些,如下:

Cursor tempCursor = Cursor.Current;


Cursor.Current = Cursors.WaitCursor;


//do Time-consuming Operations


Cursor.Current = tempCursor;

在WPF中使用:

Cursor = Cursors.Wait;


// Your Heavy work here


Cursor = Cursors.Arrow;

对于Windows窗体应用程序,可选的禁用ui控件是非常有用的。所以我的建议是这样的:

public class AppWaitCursor : IDisposable
{
private readonly Control _eventControl;


public AppWaitCursor(object eventSender = null)
{
_eventControl = eventSender as Control;
if (_eventControl != null)
_eventControl.Enabled = false;


Application.UseWaitCursor = true;
Application.DoEvents();
}


public void Dispose()
{
if (_eventControl != null)
_eventControl.Enabled = true;


Cursor.Current = Cursors.Default;
Application.UseWaitCursor = false;
}
}

用法:

private void UiControl_Click(object sender, EventArgs e)
{
using (new AppWaitCursor(sender))
{
LongRunningCall();
}
}

你可以使用:

Mouse.OverrideCursor = Cursors.Wait;

,,

Mouse.OverrideCursor = Cursors.Arrow;