2009-10-14 12 views
195

私はユーザーにログインしているC#アプリケーションを持っていますが、ハッシュアルゴリズムが高価であるため、しばらく時間がかかります。プログラムが何かをやっていることを知らせるために、Wait/Busy Cursor(通常は砂時計)をユーザに表示するにはどうすればよいですか?カーソルを待機カーソルに向けるにはどうすればよいですか?

プロジェクトはC#にあります。

答えて

334

Cursor.Currentを使用できます。ハッシュ演算は(MSDN以上2-7秒としてこれを定義する)本当に長いある場合

// Set cursor as hourglass 
Cursor.Current = Cursors.WaitCursor; 

// Execute your time-intensive hashing code here... 

// Set cursor as default arrow 
Cursor.Current = Cursors.Default; 

しかし、あなたはおそらく進捗状況をユーザに通知するためにカーソル以外の視覚的なフィードバック・インジケータを使用する必要があります。より詳細なガイドラインについては、this articleを参照してください。

編集:
@Amが指摘したように、あなたは砂時計が実際に表示されることを保証するためにCursor.Current = Cursors.WaitCursor;Application.DoEvents();を呼び出す必要があります。

+20

時間がかかるコードでメッセージループが呼び出されない場合は、カーソルを変更する必要はありません。これを有効にするには、最初のカーソルセットの後に_Application.DoEvents(); _を追加する必要があります。 – Amirshk

+13

Currentを設定した後にtry..finallyブロックを設定したい場合があります(CurrentがDefaultにリセットされることを保証します)。 – TrueWill

+6

FYI、私は上記を動作させることができませんでしたが、this.cursor = cursors.waitcursorに変更することで、出来た。 –

16

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

this.Cursor = Cursors.Wait; 

そして、スレッドの終了イベントにカーソルを復元:

次に、このようなカーソル変更

this.Cursor = Cursors.Default; 

注意、これは特定のコントロールのためにも、を行うことができますので、カーソルはなりますマウスがそれらの上にあるときだけ砂時計になります。実際

+0

彼らはバックグラウンドスレッドで行われています... – Malfist

+0

@マフィスト:良いアプローチ:)その後、あなたがする必要があるのは、リストアを終了イベントに置き、完了したことだけです。 – Amirshk

132

Cursor.Current = Cursors.WaitCursor; 

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

form.UseWaitCursor = true; 

これは、このプロパティをfalseに設定するまで、フォーム上のすべてのコントロールのカーソルを待つ表示されます。

待機カーソルを表示するもっと良い方法は、真の形式でUseWaitCursorプロパティを設定することです。 あなたが使用する必要があるアプリケーション・レベルで表示されるカーソルを待つ場合:

Application.UseWaitCursor = true; 
+0

お役立ち情報WPFで同じことをしようとしていて、* Cursor = Cursors.Wait *と* Cursor = Cursors.Arrow *で終わった。しかし、* App *の下でカーソルを見つけることができませんでした。 – itsho

+1

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

+7

これは受け入れられる解決策よりもはるかに優れています! – JPProgrammer

20

フォームまたはウィンドウレベルで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の経験のために。

+0

これは承認された回答である必要があります。それはtry-finallyを使用する唯一のものです。 –

+0

私のupvoteを持って、私は私の実装でtry-finallyを見逃していた – Jack

24

これまでに構築したアプローチ(これは頻繁に実行されるアクションなので)は、IDisposableヘルパークラスに待機カーソルコードをラップして、using()(コードの1行)、takeオプションのパラメータ、コードを実行してから、後でクリーンアップ(カーソルを復元)します。

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 
} 
+0

によって誘惑された:http://www.codeproject.com/Articles/6287/WaitCursor-hack-using-using – torno

+0

それは見たことがないアプローチ。彼は現在のカーソルをバックアップして元に戻します。これは、カーソルを重く変更している場合に便利です。 – mhapps

3

OK私は、静的な非同期メソッドを作成しました。これにより、アクションを起動してアプリケーションカーソルを変更するコントロールが無効になりました。アクションとしてタスクを実行し、終了するのを待ちます。待機中に制御が発信者に戻ります。したがって、ビジー状態のアイコンが回転している間でも、アプリケーションは反応し続けることができます。

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

ここでコード形式は、私が囮に別々のロガーを使用していた(私はNlogを使用しています)と私のメインロガーがUIに書き込んでメインフォーム

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

(リッチですテキストボックス)。私は、フォーム上の特定のコンテナ上にあるときだけ、ビジーカーソルを表示することができませんでした(しかし、私は非常に頑張っていませんでした)。すべてのコントロールには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 
1

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

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

オーケーは、他の人の見方は非常に明確であるが、私は次のように、いくつかの追加やりたい:

Cursor tempCursor = Cursor.Current; 

Cursor.Current = Cursors.WaitCursor; 

//do Time-consuming Operations   

Cursor.Current = tempCursor; 
0

使用このWPFで:

Cursor = Cursors.Wait; 

// Your Heavy work here 

Cursor = Cursors.Arrow; 
関連する問題