Как заставить курсор переходить к курсору ожидания?

У меня есть приложение С#, в котором есть пользователи, подключенные к нему, и поскольку алгоритм хэширования стоит дорого, требуется немного времени. Как я могу показать пользователю Wait/Busy 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 секунд), вам, вероятно, следует использовать индикатор визуальной обратной связи, отличный от курсора, чтобы уведомить пользователя о ходе. Более подробный набор рекомендаций см. В в этой статье.

Edit:
Как отметил @Am, вам может потребоваться позвонить Application.DoEvents(); после Cursor.Current = Cursors.WaitCursor;, чтобы убедиться, что песочные часы фактически отображаются.

Ответ 2

Собственно,

Cursor.Current = Cursors.WaitCursor;

временно устанавливает курсор Wait, но не гарантирует, что курсор Wait покажет до конца вашей операции. Другие программы или элементы управления в вашей программе могут легко reset вернуть курсор к стрелке по умолчанию, как это происходит, когда вы перемещаете мышь, пока операция все еще запущена.

Гораздо лучший способ показать курсор ожидания - установить свойство UseWaitCursor в форме в true:

form.UseWaitCursor = true;

Это отобразит курсор ожидания для всех элементов управления формы, пока вы не установите для этого свойства значение false. Если вы хотите, чтобы курсор ожидания отображался на уровне приложения, вы должны использовать:

Application.UseWaitCursor = true;

Ответ 3

Основываясь на предыдущем, мой предпочтительный подход (так как это часто выполняемое действие) заключается в том, чтобы обернуть код курсора ожидания в вспомогательный класс IDisposable, чтобы его можно было использовать с помощью() (одной строки кода), взять необязательный параметры, запустить код внутри, затем очистить (восстановить курсор) впоследствии.

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
}

Ответ 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;
        }
    }

Для лучшего опыта пользовательского интерфейса вы должны использовать Asynchrony из другого потока.

Ответ 5

Мой подход состоял бы в том, чтобы сделать все вычисления в фоновом работнике.

Затем измените курсор следующим образом:

this.Cursor = Cursors.Wait;

И в событии завершения потока верните курсор:

this.Cursor = Cursors.Default;

Примечание. Это также можно сделать для определенных элементов управления, поэтому курсор будет песочным часом, только когда мышь над ними.

Ответ 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), и мой главный регистратор пишет в пользовательский интерфейс (богатое текстовое поле). Мне не удалось отобразить занятый курсор только тогда, когда над конкретным контейнером в форме (но я не очень старался). Все элементы управления имеют свойство 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

Okey, вид других людей очень ясен, но я хотел бы добавить некоторые, как следует:

Cursor tempCursor = Cursor.Current;

Cursor.Current = Cursors.WaitCursor;

//do Time-consuming Operations         

Cursor.Current = tempCursor;

Ответ 8

С приведенным ниже классом вы можете сделать предложение понтона "безопасным исключением".

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

Ответ 9

Используйте это с WPF:

Cursor = Cursors.Wait;

// Your Heavy work here

Cursor = Cursors.Arrow;

Ответ 10

Для приложений Windows Forms может быть полезно дополнительное отключение 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();
    }
}