如何让光标变成等待光标?

问题描述 投票:0回答:13

如何向用户显示等待/忙碌光标(通常是沙漏),让他们知道程序正在执行某些操作?

c# .net winforms user-interface mouse-cursor
13个回答
549
投票

您可以使用

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;
 以确保沙漏实际显示。 


198
投票
事实上,

Cursor.Current = Cursors.WaitCursor;

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

显示等待光标的更好方法是将表单中的 UseWaitCursor 属性设置为 true:

form.UseWaitCursor = true;

这将为表单上的所有控件显示等待光标,直到您将此属性设置为 false。 如果您希望等待光标显示在应用程序级别,您应该使用:

Application.UseWaitCursor = true;
    

42
投票
在前一种方法的基础上,我的首选方法(因为这是一个经常执行的操作)是将等待光标代码包装在 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 }
    

36
投票
在窗体或窗口级别使用

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 体验,您应该从不同的线程使用异步。


26
投票
我的方法是在后台工作人员中进行所有计算。

然后像这样改变光标:

this.Cursor = Cursors.Wait;

并在线程的完成事件中恢复光标:

this.Cursor = Cursors.Default;

注意,这也可以针对特定控件执行,因此仅当鼠标位于其上方时光标才会是沙漏。


10
投票
对于 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(); } }
    

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

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),而我的主记录器正在写入 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
    

5
投票
好吧,其他人的观点已经很清楚了,但我想补充一些,如下:

Cursor tempCursor = Cursor.Current; Cursor.Current = Cursors.WaitCursor; //do Time-consuming Operations Cursor.Current = tempCursor;
    

3
投票
通过下面的课程,您可以提出 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; }
    

3
投票

与 WPF 一起使用:

Cursor = Cursors.Wait; // Your Heavy work here Cursor = Cursors.Arrow;
    

0
投票
您可以使用:

Mouse.OverrideCursor = Cursors.Wait;
&&

Mouse.OverrideCursor = Cursors.Arrow;
    

0
投票
一个简单的

ScopeGuard

就是一个好帮手:

public record ScopeGuard(Action Action) : IDisposable { public void Dispose() { Action.Invoke(); } }
然后

Enabled = false; Application.UseWaitCursor = true; Application.DoEvents(); using ScopeGuard sg = new(() => { Application.UseWaitCursor = false; Enabled = true; }); // logic goes here
    

0
投票
.Net Windows 应用程序中对我有用的内容:

Cursor = Cursors.WaitCursor; ... Cursor = Cursors.Default;
不需要

Application.DoEvents();

© www.soinside.com 2019 - 2024. All rights reserved.