[WPF] ボタン押下時の処理中に画面操作を受け付けなくする

C# には async/await という素晴らしい仕組みがあり、時間のかかる処理を行う際にもアプリそのものは固まらないようにすることが簡単にできます。
ここでアプリが固まるというのは、いわゆるタイトルバーに「応答なし」と出るヤツです。

■ async/await を使用して「応答なし」にならないボタン

例えば次のように書けば 5 秒かかる処理をボタン押下で行っても「応答なし」にはなりません。
ここでは xaml に、x:Name=”button” というボタンがあると思ってください。

private async void button_Click(object sender, RoutedEventArgs e)
{
    MessageBox.Show("Start!");
    await Task.Delay(5000);
    MessageBox.Show("End!!");
}

これで、「応答なし」にならずに、5 秒間の処理ができます。
しかし、「応答なし」にならない代わりに、処理中でもボタンを何度も押せてしまいます。

人力でクリックできる速度より早く完了する処理であれば問題はないでしょう。
しかし、時間がかかる処理を行う場合は、連打されると都合が悪い場合もあるでしょう。
ボタン連打ばかりでなく、他のボタンを押されるなどでいろいろ整合性が取れなくなってしまう場合などもあるでしょう。

今回は、豪快に処理中は Window 内の UI 全体を使用不可にしてみましょう。

■ 基本方針

既に、ボタン押下時の処理は(コードビハインドに)実装されているものとします。
それも何か所も存在するし、今後も増えて行くとします。
これをなるべく省力で変更する方法として、Func を受け取り、Func の実行前に UI をロックし、Func の実行後に UI のロックを解除するメソッドを作りこれを使用するようにします。

■ 処理中に UI をロックするコード

まず、LockUI メソッドを次のように作ります。今回は、マウスカーソルもグルグルするようにしてみました。
次に button_Click2 メソッドを作り、button_Click メソッドの内容を移動します。
最後に、button_Click の中で、LockUI を button_Click2 を引数に渡し実行します。

private async Task LockUI(Func<Task> act)
{
    var topElm = ((UIElement)VisualTreeHelper.GetChild(this, 0));
    var oldEnabled = topElm.IsEnabled;
    var oldCursor = this.Cursor;
    try
    {
        this.Cursor = Cursors.Wait;
        topElm.IsEnabled = false;
        await act();
    }
    finally
    {
        topElm.IsEnabled = oldEnabled;
        this.Cursor = oldCursor;
    }
}

private async void button_Click(object sender, RoutedEventArgs e)
{
    await LockUI(async ()=> { await button_Click2(); });
}

private async Task button_Click2()
{
    MessageBox.Show("Start!");
    await Task.Delay(5000);
    MessageBox.Show("End!!");
}

これで、処理中に UI がロックされるボタンができました。

広告

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト /  変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト /  変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト /  変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト /  変更 )

%s と連携中