【问题标题】:How can I make the cursor turn to the wait cursor?如何使光标变为等待光标?
【发布时间】:2010-12-06 19:34:09
【问题描述】:

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

【问题讨论】:

    标签: c# .net winforms user-interface mouse-cursor


    【解决方案1】:

    您可以使用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 秒),您可能应该使用光标以外的视觉反馈指示器来通知用户进步。如需更深入的指南,请参阅this article

    编辑:
    正如@Am 指出的那样,您可能需要在Cursor.Current = Cursors.WaitCursor; 之后调用Application.DoEvents(); 以确保沙漏实际显示。

    【讨论】:

    • 这不需要更改光标 - 如果在时间密集型代码期间不会调用消息循环。要启用它,您需要在第一个光标集之后添加 Application.DoEvents();
    • 您可能还想在设置 Current 后尝试 try..finally 阻止(确保 Current 被重置为默认值)。
    • 仅供参考,我无法让上述工作,但通过将其更改为 this.cursor = cursors.waitcursor;它奏效了。
    • 如果我在 Cursor.Current = Cursors.WaitCursor 之后使用 Application.DoEvents() 则不会显示沙漏但是,它确实在没有 Application.DoEvents() 的情况下工作。不知道为什么
    • 最好使用Application.UseWaitCursor = trueApplication.UseWaitCursor = false
    【解决方案2】:

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

    然后像这样改变光标:

    this.Cursor = Cursors.Wait;
    

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

    this.Cursor = Cursors.Default;
    

    注意,这也可以用于特定控件,因此只有当鼠标在它们上方时,光标才会是沙漏。

    【讨论】:

    • @Malfist:好方法 :),那么您需要做的就是将还原放在结束事件中,然后就完成了。
    • 如果我们需要响应式 UI 并通知某些东西正在运行,这是一个干净的解决方案。
    【解决方案3】:

    其实,

    Cursor.Current = Cursors.WaitCursor;
    

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

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

    form.UseWaitCursor = true;
    

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

    Application.UseWaitCursor = true;
    

    【讨论】:

    • 很高兴知道。我试图在 WPF 中做同样的事情,结果是 Cursor = Cursors.WaitCursor = Cursors.Arrow。但我在 App 下找不到光标
    • 在 Application 下找不到 UseWaitCursor !
    • 我发现,在操作结束时设置 form.UseWaitCursor = false 时,它​​实际上并没有重置光标,直到您移动或单击鼠标。 OTOH,form.Cursor 没有这个问题。我根本无法让 Cursor.Current 工作。
    • 如果你想使用等待光标,一定要使用这个。我试过Cursor.Current,但由于某种原因它一直被立即重置,这个解决方案使等待光标一直存在,直到更改为false
    【解决方案4】:

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

    【讨论】:

    • 这应该是接受的答案。它是唯一使用 try-finally 的。
    • 有我的支持,我在我的实现中错过了一个 try-finally
    【解决方案5】:

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

    【讨论】:

    【解决方案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
    

    【讨论】:

      【解决方案7】:

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

      【讨论】:

        【解决方案8】:

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

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

        【讨论】:

          【解决方案9】:

          将此与 WPF 一起使用:

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

          【讨论】:

          • 我喜欢你的回答,但我建议将它放在 try / finally 的上下文中。我多年来一直使用 Donut 接受的答案,但它在 C# .NET Core 3.1 WPF 应用程序的代码中不起作用,就像你的答案一样。在 .NET Core 中,微软改变了与光标相关的界面和对象。
          【解决方案10】:

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2018-04-08
            • 1970-01-01
            • 1970-01-01
            • 2011-01-08
            • 1970-01-01
            • 2012-11-09
            • 1970-01-01
            相关资源
            最近更新 更多