287

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

4

10 回答 10

510

您可以使用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;

于 2009-10-14T19:49:37.677 回答
186

实际上,

Cursor.Current = Cursors.WaitCursor;

临时设置等待光标,但不确保等待光标显示直到操作结束。程序中的其他程序或控件可以轻松地将光标重置回默认箭头,这实际上是在操作仍在运行时移动鼠标时发生的。

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

form.UseWaitCursor = true;

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

Application.UseWaitCursor = true;
于 2010-09-11T10:13:20.807 回答
41

在前面的基础上,我的首选方法(因为这是一个经常执行的操作)是将等待光标代码包装在 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
}
于 2015-05-22T13:03:17.473 回答
34

在窗体或窗口级别使用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 体验,您应该使用来自不同线程的异步。

于 2015-04-03T14:29:56.227 回答
23

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

然后像这样改变光标:

this.Cursor = Cursors.Wait;

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

this.Cursor = Cursors.Default;

请注意,这也可以针对特定控件完成,因此只有当鼠标位于它们上方时,光标才会是沙漏。

于 2009-10-14T19:52:22.693 回答
4

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

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
于 2016-08-04T01:06:07.920 回答
4

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

Cursor tempCursor = Cursor.Current;

Cursor.Current = Cursors.WaitCursor;

//do Time-consuming Operations         

Cursor.Current = tempCursor;
于 2017-07-19T01:28:12.677 回答
3

对于 Windows 窗体应用程序,可选择禁用 UI-Control 非常有用。所以我的建议是这样的:

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();
    }
}
于 2019-05-21T10:19:57.650 回答
3

将此与 WPF 一起使用:

Cursor = Cursors.Wait;

// Your Heavy work here

Cursor = Cursors.Arrow;
于 2018-01-29T12:50:47.677 回答
2

通过下面的课程,您可以提出 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;
}
于 2016-11-04T08:19:19.637 回答