0

私のWinformsアプリケーションでは、操作を開始すると、非同期で実行される場合と実行されない場合があります。作業がすべて同期的に行われる場合、作業が非同期的に行われる場合と同じアプローチを使用して待機カーソルを表示することはできません。

問題を示す例を次に示します。

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        var t = ButtonClick(button1, DoWorkAsync1);
    }

    private void button2_Click(object sender, EventArgs e)
    {
        var t = ButtonClick(button2, DoWorkAsync2);
    }

    private async Task ButtonClick(Button btn, Func<Task> doWorkAsync)
    {
        // Disable the UI and show the wait cursor
        btn.Text = "Working...";
        btn.Enabled = false;
        UseWaitCursor = true;

        // Application.DoEvents(); // Inserting this causes the problem to go away.
        await doWorkAsync();

        // Simulate an update of the UI taking a long time
        Thread.Sleep(2000); 

        // Renable the UI and stop showing the wait cursor
        UseWaitCursor = false;
        btn.Enabled = true;
    }

    private Task DoWorkAsync1()
    {
        // Work takes some time
        return Task.Delay(2000); 
    }

    private Task DoWorkAsync2()
    {
        // Work doesn't take any time, results are immediately available
        return Task.FromResult<int>(0); 
    }
}

この例では:

  • button1 をクリックすると、Wait カーソルが表示されます (作業が非同期で行われるため)。
  • button2 をクリックしても、待機カーソルは表示されませ(作業はすべて同期的に行われるため)。
  • button1 と button2 のいずれかをクリックすると、期待どおりに UI が無効になります。

望ましいのは、button1 または button2 のいずれかをクリックすると、ボタンをクリックしてから UI 更新作業が完了するまでの間、Wait カーソルが表示されるようにすることです。

質問:

  • を挿入せずにこれを解決する方法はありApplication.DoEventますか (または、メッセージのポンピングを発生させる同等のものはありません)、またはメッセージをポンピングすることによってのみ可能です。
  • 余談ですが、コントロールの無効化が正しく機能するのはなぜですか(カーソルとは異なります)。
4

2 に答える 2

0

DoEvents投げ込む代わりに:

await Task.Run(() => { });

これにより、待機中のタスクが開始時に正しく終了しないことが保証されるため、メソッドの残りの部分は同期ではなく継続として実行されます。

doWorkAsyncまたは、同期的に実行されないように継続を直接追加することもできます。

await doWorkAsync
    .ContinueWith(t => { }, TaskContinuationOptions.HideScheduler);
于 2013-08-20T18:36:34.790 に答える
0

バックグラウンド スレッドで実行したい同期作業がある場合は、それを で呼び出してからTask.Run結果awaitを返します (呼び出しコードはそれを非同期として扱います)。

private Task DoWorkAsync1()
{
    return Task.Delay(2000); 
}

private Task DoWorkAsync2()
{
    return Task.Run(() => Thread.Sleep(2000));
}

あるいは、同期作業が本当に即時であり、他の UI 作業の前にいくつかの UI 更新 (カーソルなど) を本当に適用したい場合は、単純に次のようにすることができます。

await Task.Delay(10);
于 2013-08-20T18:27:52.410 に答える