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

发布于 2024-08-08 15:57:02 字数 45 浏览 3 评论 0原文

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

How can I display the Wait/Busy Cursor (usually the hourglass) to the user to let them know the program is doing something?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(13

灯角 2024-08-15 15:57:02

您可以使用 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 指出的,您可能需要在 Cursor.Current = Cursors.WaitCursor; 之后调用 Application.DoEvents(); 以确保沙漏实际显示。

You can use 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;

However, if the hashing operation is really lengthy (MSDN defines this as more than 2-7 seconds), you should probably use a visual feedback indicator other than the cursor to notify the user of the progress. For a more in-depth set of guidelines, see this article.

Edit:
As @Am pointed out, you may need to call Application.DoEvents(); after Cursor.Current = Cursors.WaitCursor; to ensure that the hourglass is actually displayed.

清音悠歌 2024-08-15 15:57:02

实际上,

Cursor.Current = Cursors.WaitCursor;

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

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

form.UseWaitCursor = true;

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

Application.UseWaitCursor = true;

Actually,

Cursor.Current = Cursors.WaitCursor;

temporarily sets the Wait cursor, but doesn’t ensure that the Wait cursor shows until the end of your operation. Other programs or controls within your program can easily reset the cursor back to the default arrow as in fact happens when you move mouse while operation is still running.

A much better way to show the Wait cursor is to set the UseWaitCursor property in a form to true:

form.UseWaitCursor = true;

This will display wait cursor for all controls on the form until you set this property to false.
If you want wait cursor to be shown on Application level you should use:

Application.UseWaitCursor = true;
爱你不解释 2024-08-15 15:57:02

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

Building on the previous, my preferred approach (since this is a frequently performed action) is to wrap the wait cursor code in an IDisposable helper class so it can be used with using() (one line of code), take optional parameters, run the code within, then clean up (restore cursor) afterwards.

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

Usage:

using (new CursorWait())
{
    // Perform some code that shows cursor
}
木槿暧夏七纪年 2024-08-15 15:57:02

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

It is easier to use UseWaitCursor at the Form or Window level.
A typical use case can look like below:

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

For a better UI experience you should use Asynchrony from a different thread.

花之痕靓丽 2024-08-15 15:57:02

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

然后像这样更改光标:

this.Cursor = Cursors.Wait;

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

this.Cursor = Cursors.Default;

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

My approach would be to make all the calculations in a background worker.

Then change the cursor like this:

this.Cursor = Cursors.Wait;

And in the thread's finish event restore the cursor:

this.Cursor = Cursors.Default;

Note, this can also be done for specific controls, so the cursor will be the hourglass only when the mouse is above them.

始终不够 2024-08-15 15:57:02

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

For Windows Forms applications an optional disabling of a UI-Control can be very useful. So my suggestion looks like this:

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

Usage:

private void UiControl_Click(object sender, EventArgs e)
{
    using (new AppWaitCursor(sender))
    {
        LongRunningCall();
    }
}
漆黑的白昼 2024-08-15 15:57:02

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

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

OK so I created a static async method. That disabled the control that launches the action and changes the application cursor. It runs the action as a task and waits for to finish. Control returns to the caller while it waits. So the application remains responsive, even while the busy icon spins.

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

Here's the code form the main form

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

I had to use a separate logger for the dummy action (I am using Nlog) and my main logger is writing to the UI (a rich text box). I wasn't able to get the busy cursor show only when over a particular container on the form (but I didn't try very hard.) All controls have a UseWaitCursor property, but it doesn't seem have any effect on the controls I tried (maybe because they weren't on top?)

Here's the main log, which shows things happening in the order we expect:

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
夜唯美灬不弃 2024-08-15 15:57:02

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

Cursor tempCursor = Cursor.Current;

Cursor.Current = Cursors.WaitCursor;

//do Time-consuming Operations         

Cursor.Current = tempCursor;

Okey,Other people's view are very clear, but I would like to do some added, as follow:

Cursor tempCursor = Cursor.Current;

Cursor.Current = Cursors.WaitCursor;

//do Time-consuming Operations         

Cursor.Current = tempCursor;
审判长 2024-08-15 15:57:02

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

With the class below you can make the suggestion of Donut "exception safe".

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

the class 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;
}
野稚 2024-08-15 15:57:02

将其与 WPF 一起使用:

Cursor = Cursors.Wait;

// Your Heavy work here

Cursor = Cursors.Arrow;

Use this with WPF:

Cursor = Cursors.Wait;

// Your Heavy work here

Cursor = Cursors.Arrow;
深海蓝天 2024-08-15 15:57:02

您可以使用:

Mouse.OverrideCursor = Cursors.Wait; 

&&

Mouse.OverrideCursor = Cursors.Arrow;

You can use:

Mouse.OverrideCursor = Cursors.Wait; 

&&

Mouse.OverrideCursor = Cursors.Arrow;
空气里的味道 2024-08-15 15:57:02

一个简单的 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

A simple ScopeGuard is a nice helper:

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

and then

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

    // logic goes here
你又不是我 2024-08-15 15:57:02

在 .Net Windows 应用程序中对我有用的是:

Cursor = Cursors.WaitCursor;
...
Cursor = Cursors.Default;

不需要 Application.DoEvents();

What worked for me in a .Net Windows Application:

Cursor = Cursors.WaitCursor;
...
Cursor = Cursors.Default;

No need for Application.DoEvents();.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文