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


263

我有一个C#应用程序,有用户登录到该应用程序,并且由于哈希算法昂贵,因此需要花费一些时间。如何向用户显示“等待/忙碌光标”(通常是沙漏),以使他们知道程序正在执行操作?

该项目在C#中。

Answers:


451

您可以使用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;以确保实际显示了沙漏。


23
如果在耗时的代码中不会调用消息循环,则不必更改游标。要启用它,您需要添加Application.DoEvents();。在第一个光标设置之后。
Amirshk,2009年

16
您可能还希望在设置Current之后尝试try..finally块(确保将Current重置为Default)。
TrueWill

7
仅供参考,我无法使以上内容正常工作,但是将其更改为this.cursor = cursors.waitcursor; 有效。
汉斯·鲁德尔

4
如果在Cursor.Current = Cursors.WaitCursor之后使用Application.DoEvents(),则不会显示沙漏,但是,在没有Application.DoEvents()的情况下它确实可以工作。不确定为什么
Vbp

14
这是更好地使用Application.UseWaitCursor = trueApplication.UseWaitCursor = false
Gianpiero

169

其实,

Cursor.Current = Cursors.WaitCursor;

临时设置“等待”光标,但不能确保在操作结束之前一直显示“等待”光标。实际上,当您在操作仍在运行时移动鼠标时,其他程序或程序中的控件可以轻松地将光标重置为默认箭头。

显示“等待”光标的一种更好的方法是将窗体中的UseWaitCursor属性设置为true:

form.UseWaitCursor = true;

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

Application.UseWaitCursor = true;

很高兴知道。我试图在WPF中做同样的事情,最后得到Cursor = Cursors.WaitCursor = Cursors.Arrow。但我在App
itsho

2
在“应用程序”下找不到UseWaitCursor!
Chandra Eskay

我发现,在操作结束时设置form.UseWaitCursor = false时,它实际上并不会重置光标,除非您移动或单击鼠标。OTOH,form.Cursor没有这个问题。我根本无法使用Cursor.Current。
斯图尔特

39

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


没看到,但是是类似的方法。他先备份当前光标,然后还原它,如果您要进行大量的光标更改,这可能会很有用。
mhapps,2015年

27

在Form或Window级别使用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体验,您应该从其他线程使用Asynchrony。


2
这应该是接受的答案。这是唯一使用try-finally的方法。
约翰·亨克尔

1
投票赞成,我在实现过程中最后一次尝试失误
杰克

19

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

然后像这样更改光标:

this.Cursor = Cursors.Wait;

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

this.Cursor = Cursors.Default;

请注意,也可以对特定控件执行此操作,因此,只有当鼠标位于其上方时,光标才会成为沙漏。


@Malfist:一种好方法:),那么您需要做的就是将还原放入结束事件,然后完成。
2009年

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

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;
}

2

Okey,其他人的观点很明确,但是我想做一些补充,如下:

Cursor tempCursor = Cursor.Current;

Cursor.Current = Cursors.WaitCursor;

//do Time-consuming Operations         

Cursor.Current = tempCursor;

2

对于Windows Forms应用程序,可选地禁用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();
    }
}

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.