2013年10月11日金曜日

WPFの描画処理の実行順について

下記のコードはLabel1の表示を更新してから3秒間UIスレッドをフリーズさせて処理が戻ることを期待している。
private void button1_Click(object sender, RoutedEventArgs e)
{
    this.Label1.Content = DateTime.Now.ToString();
    System.Threading.Thread.Sleep(3000);
}
しかし結果はそうはならない。3秒間フリーズした後に表示が更新されてUIに処理が戻る。
これはLabel1.Contentプロパティの値自体は意図したタイミングで更新されるが、実際の描画処理はPriority=7(Render)でキューイングされるがこの優先順位はPriority=9(Normal)より低いためイベントハンドラを抜けた後に実行される。つまりSleep()が先に実行される。

これが問題になるのは処理に時間がかかる場合なので別スレッドからInvoke/BeginInvokeする時にPriorityを変更してやればいい(通常変更する必要も無いが)。 ともかくこの現象を知っておかないと「おや?」と思う挙動をすることがあるので注意。