回答:
使用できます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;
ます。
Application.UseWaitCursor = true
をおApplication.UseWaitCursor = false
実は
Cursor.Current = Cursors.WaitCursor;
は一時的に待機カーソルを設定しますが、操作が終了するまで待機カーソルが表示されることは保証されません。プログラム内の他のプログラムまたはコントロールは、操作の実行中にマウスを動かすと、カーソルをデフォルトの矢印に簡単にリセットできます。
待機カーソルを表示するより良い方法は、フォームのUseWaitCursorプロパティをtrueに設定することです。
form.UseWaitCursor = true;
このプロパティをfalseに設定するまで、フォーム上のすべてのコントロールの待機カーソルが表示されます。待機カーソルをアプリケーションレベルで表示したい場合は、以下を使用する必要があります。
Application.UseWaitCursor = true;
前に構築した、私の推奨するアプローチ(これは頻繁に実行されるアクションであるため)は、待機カーソルコードを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
}
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エクスペリエンスを向上させるには、別のスレッドから非同期を使用する必要があります。
静的な非同期メソッドを作成しました。これにより、アクションを起動してアプリケーションカーソルを変更するコントロールが無効になりました。アクションをタスクとして実行し、終了を待ちます。待機している間、制御は呼び出し元に戻ります。そのため、ビジーアイコンが回転している間でも、アプリケーションは応答性を維持します。
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
以下のクラスを使用すると、ドーナツの提案を「例外安全」にすることができます。
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;
}
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();
}
}
これをWPFで使用します。
Cursor = Cursors.Wait;
// Your Heavy work here
Cursor = Cursors.Arrow;