Як змусити курсор звернутися до курсору очікування?


263

У мене є додаток C #, в яке користувачі входять у нього, і оскільки алгоритм хешування є дорогим, потрібно зробити трохи часу. Як я можу відобразити користувачеві курсор очікування / зайнятості (зазвичай пісочний годинник), щоб він міг знати, що програма щось робить?

Проект знаходиться в C #.

Відповіді:


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

16
Напевно, ви хочете спробувати .. остаточно заблокувати після встановлення струму (гарантуючи, що струм буде скинутий до замовчування).
TrueWill

7
FYI, я не міг змусити вищезазначене працювати, але змінивши його на this.cursor = cursors.waitcursor; це спрацювало.
Ганс Рудель

4
Пісочний годинник не відображався, якщо я використовував Application.DoEvents () після Cursor.Current = Cursors.WaitCursor Однак він працював без Application.DoEvents (). Не впевнений Чому
Vbp

14
Краще використовувати Application.UseWaitCursor = trueіApplication.UseWaitCursor = false
Gianpiero

169

Насправді,

Cursor.Current = Cursors.WaitCursor;

тимчасово встановлює курсор Wait, але не забезпечує показ курсора Wait до кінця вашої операції. Інші програми або елементи керування у вашій програмі можуть легко скинути курсор назад до стрілки за замовчуванням, як це відбувається насправді під час переміщення миші, поки операція все ще працює.

Набагато кращий спосіб показати курсор Wait - встановити властивість UseWaitCursor у формі в true:

form.UseWaitCursor = true;

Це покаже курсор очікування для всіх елементів керування у формі, поки ви не встановите для цього властивості значення false. Якщо ви хочете, щоб курсор очікування відображався на рівні програми, ви повинні використовувати:

Application.UseWaitCursor = true;

Добре знати. Я намагався зробити те ж саме в WPF, і в кінцевому підсумку вийшов Cursor = Cursors.Wait і Cursor = Cursors.Arrow . Але я не міг знайти курсор під App
itsho

2
Не вдалося знайти UseWaitCursor у програмі!
Чандра Ескай

Я встановив, що, встановивши form.UseWaitCursor = false в кінці операції, він фактично не скидає курсор, поки ви не перемістите або натисніть мишкою. OTOH, form.Cursor не має цієї проблеми. Я не міг змусити Cursor.Current працювати взагалі.
Стюарт

39

Спираючись на попередній, мій кращий підхід (оскільки це часто виконувана дія) полягає в загортанні коду курсора очікування в ідентифікаційний клас помічника, щоб його можна було використовувати за допомогою () (один рядок коду), приймати необов'язкові параметри, запускати код всередині, а потім очистіть (відновіть курсор) після цього.

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

27

Простіше використовувати 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 з іншої нитки.


2
Це має бути прийнята відповідь. Це єдиний, хто використовує спробу.
Джон Генкель

1
У мене є підсумок, я пропустив спробу в остаточному виконанні
Джек

19

Мій підхід полягає в тому, щоб зробити всі розрахунки у фоновому працівнику.

Потім змініть курсор так:

this.Cursor = Cursors.Wait;

І в події закінчення потоку відновіть курсор:

this.Cursor = Cursors.Default;

Зверніть увагу, це також можна зробити для конкретних елементів керування, тому курсор буде пісочним годинником лише тоді, коли миша над ними.


@Malfist: гарний підхід :), тоді все, що вам потрібно зробити, це поставити відновлення в кінцевий захід, і ваше завершення.
Аміршк

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), і мій основний реєстратор пише в інтерфейс (багате текстове поле). Я не зміг отримати показ зайнятого курсору лише тоді, коли над певним контейнером у формі (але я не дуже старався.) Усі елементи управління мають властивість 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

Клас нижче, ви можете зробити пропозицію пончика "винятком безпечним".

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

Гаразд, погляд інших людей дуже чіткий, але я хотів би зробити деякі доповнені, як слід:

Cursor tempCursor = Cursor.Current;

Cursor.Current = Cursors.WaitCursor;

//do Time-consuming Operations         

Cursor.Current = tempCursor;

2

Для програм 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();
    }
}

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.