画面デザイン上で DialogResult をセットしているボタンのイベントで、非同期処理をおこなうと、画面が消えてしまう現象の対応

タイトルは日本語でおkなので、以下に状況を記載しますね。めちゃくちゃ謎現象で長期間さっぱりだったのですが、やっと対策が見つかったので一安心です。

目次

仕様回り

C#, Winforms な画面で、サブ画面で設定をごにょごにょしたり、進捗状況画面出しながら裏で処理したり、するのですが、これが重たい処理なので、応答なしになってしまうので、非同期対応することにしました。

Form1 のボタンクリックで Form2 をモーダル表示して、画面上で何かやって、OK ボタンを押します。今回は、この OK ボタンのクリックイベントで、重たい処理を非同期対応します。

Form1 の画面デザイン

f:id:sutefu7:20200903223443p:plain

Form2 の画面デザイン

後で Form1 で何ボタンを押したかチェックするので、OK ボタン、キャンセルボタンには、画面デザイン上で DialogResult をセットしています。

f:id:sutefu7:20200903223456p:plain

Form2 の処理内容

private async void button1_Click(object sender, EventArgs e)
{
    await Task.Run(async() =>
    {
        // 非同期内で、何らかの重たい処理をこなす
        for (var i = 0; i < 5; i++)
        {
            await Task.Delay(500);
        }
    });

    Close();
}

private void button2_Click(object sender, EventArgs e)
{
    Close();
}

実行すると・・・

非同期対応後、実行確認してみると、OK ボタンを押したタイミングで、画面がすぐに消えてしまいます。

f:id:sutefu7:20200903223511p:plain

なぜなの?

で、結論としては、原因は、画面デザイン上でセットしてしまった DialogResult が原因らしくて、画面デザイン上では初期値のまま(DialogResult.None)にしておいて、画面を閉じる直前とかに、コード上で DialogResult = DialogResult.OK; とかセットするのが良いみたいです。

画面デザイン上で DialogResult をセットする

ちなみに、画面デザイン上で DialogResult をセットしたボタンは、クリックイベントを書かなくても画面を閉じることができます!びっくり!今まで何十年と、クリックイベントと Close(); を書いてたよ・・・。

DialogResult って画面を閉じた後、どのボタンを押したのかを知るための機能だと思っていましたが、イベントを書かずに画面を閉じることができるという、隠された機能もあったんですね!

あと、DialogResult 設定済みのボタンと画面と、非同期処理の関係性がいまいちよくわからんですが、つまり、どうしてこういう仕様なのかは分かりませんが、対策としてはコード上で扱いましょうね、ということが分かりました。