カーソルを待機カーソルに変えるにはどうすればよいですか?


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();を追加する必要があります。最初のカーソルセットの後。
アミールシュク

16
おそらく、Currentの設定後にtry..finallyブロックも必要です(Currentがデフォルトにリセットされることを保証します)。
TrueWill、2009

7
参考までに、上記を機能させることはできませんでしたが、this.cursor = cursors.waitcursor;に変更することで、出来た。
Hans Rudel

4
Cursor.Current = Cursors.WaitCursorの後にApplication.DoEvents()を使用した場合、砂時計は表示されませんでしたが、Application.DoEvents()がなくても機能しました。理由不明
Vbp

14
それを使用することApplication.UseWaitCursor = trueをおApplication.UseWaitCursor = false
勧め

169

実は

Cursor.Current = Cursors.WaitCursor;

は一時的に待機カーソルを設定しますが、操作が終了するまで待機カーソルが表示されることは保証されません。プログラム内の他のプログラムまたはコントロールは、操作の実行中にマウスを動かすと、カーソルをデフォルトの矢印に簡単にリセットできます。

待機カーソルを表示するより良い方法は、フォームのUseWaitCursorプロパティをtrueに設定することです。

form.UseWaitCursor = true;

このプロパティをfalseに設定するまで、フォーム上のすべてのコントロールの待機カーソルが表示されます。待機カーソルをアプリケーションレベルで表示したい場合は、以下を使用する必要があります。

Application.UseWaitCursor = true;

知っておくと良い。私はWPFで同じことをしようとしていて、結局Cursor = Cursors.WaitおよびCursor = Cursors.Arrowになりました。しかし、Appの
itho 2013

2
アプリケーションの下にUseWaitCursorが見つかりませんでした!
Chandra Eskay 2015年

操作の最後でform.UseWaitCursor = falseを設定すると、移動またはマウスをクリックするまで実際にカーソルがリセットされないことがわかりました。OTOH、form.Cursorにはこの問題はありません。Cursor.Currentをまったく動作させることができませんでした。
スチュワート

39

前に構築した、私の推奨するアプローチ(これは頻繁に実行されるアクションであるため)は、待機カーソルコードをIDisposableヘルパークラスでラップして、using()(コードの1行)で使用できるようにし、オプションのパラメーターを取得して実行することです内のコード、その後クリーンアップ(復元カーソル)。

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

29

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エクスペリエンスを向上させるには、別のスレッドから非同期を使用する必要があります。


2
これは受け入れられた答えである必要があります。それはtry-finallyを使用する唯一のものです。
John Henckel、

1
私の賛成投票があります。実装にトライファイナルがありませんでした
Jack

19

私のアプローチは、すべての計算をバックグラウンドワーカーで行うことです。

次に、次のようにカーソルを変更します。

this.Cursor = Cursors.Wait;

スレッドの終了イベントでカーソルを復元します。

this.Cursor = Cursors.Default;

これは特定のコントロールに対しても実行できるため、カーソルが砂時計の上にあるときのみ、カーソルが砂時計になります。


@マルフィスト:良いアプローチ:)、その後、あなたがする必要があるのは、最後のイベントに復元を配置し、あなたが完了したことです。
Amirshk

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

以下のクラスを使用すると、ドーナツの提案を「例外安全」にすることができます。

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フォームアプリケーションの場合、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();
    }
}

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.