我怎样才能使光标转向等待光标?
我有一个C#应用程序,有用户login到它,因为哈希algorithm是昂贵的,它需要一点时间做。 如何显示等待/忙碌光标(通常是沙漏)到用户让他们知道该程序正在做什么?
该项目是在C#中。
你可以使用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秒),则应该使用光标以外的可视反馈指示器来通知用户进度。 有关更为深入的指导方针,请参阅本文 。
编辑:
正如@Am指出的,你可能需要调用Application.DoEvents();
Cursor.Current = Cursors.WaitCursor;
确保沙漏实际显示。
其实,
Cursor.Current = Cursors.WaitCursor;
临时设置等待游标,但不能确保等待游标显示,直到您的操作结束。 程序中的其他程序或控件可以轻松地将光标重置为默认箭头,因为在操作仍在运行时移动鼠标实际上会发生这种情况。
显示等待游标的更好方法是将窗体中的UseWaitCursor属性设置为true:
form.UseWaitCursor = true;
这将显示窗体上的所有控件的等待光标,直到您将此属性设置为false。 如果您希望在应用程序级别显示等待光标,则应使用:
Application.UseWaitCursor = true;
基于以前,我首选的方法(因为这是一个经常执行的操作)是将等待游标代码包装在IDisposable助手类中,以便可以使用()(一行代码),可选参数,运行内部的代码,然后清理(恢复光标)。
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 }
在窗体或窗口级别使用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体验,您应该使用来自其他线程的asynchronous。
我的方法是在后台工作人员进行所有的计算。
然后像这样改变光标:
this.Cursor = Cursors.Wait;
并在线程完成事件恢复光标:
this.Cursor = Cursors.Default;
请注意,对于特定的控件也可以这样做,所以只有当鼠标在鼠标上方时,光标才会是沙漏。
好,所以我创build了一个静态的asynchronous方法。 禁用启动动作的控件并更改应用程序的光标。 它作为一项任务运行,等待完成。 控制权在等待时返回给调用者。 所以即使忙图标旋转,应用程序仍然保持响应。
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; }
这是主窗体的代码forms
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 { } }
我不得不使用一个单独的logging器来做假动作(我正在使用Nlog),而且我的主logging器正在写入UI(一个富文本框)。 我无法获得繁忙的光标显示只有在窗体上的特定容器(但我没有很努力)。所有控件都有一个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
下面的课程,你可以提出甜甜圈的build议“exception安全”。
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;