DependencyObjectって その5で書いたように、DependencyObject(正確にはDispatcherObject)の子供たちの依存プロパティとかはインスタンスを生成したスレッド以外からは安全に使用できない。
んじゃ時間のかかる処理ってどうするの?っていう疑問があがるんだけど、これはWindowsFormsのときと同じ方法でいける。
つまりBackgroundWorkerを使うと楽チンだよって書いてあった。
ってことで、ボタンを押すと10秒間スリープしてテキストブロックにComplete!って表示するものを作ってみる。
まずは、XAML。テキストブロックとボタンが置いてあるだけのシンプルなものです。
テキストブロックに実行結果を表示するので、NameをつけてC#のコードからアクセスできるようにしてある。
<Window x:Class="WpfThread.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Name="textBlock"
Text="テキストですよ"
Grid.Row="0"/>
<Button Content="時間のかかる処理"
Click="Button_Click"
Grid.Row="1" />
</Grid>
</Window>
ボタンのクリックイベントのあるWindow1.xaml.csのコードは下のような感じ。
BackgroundWorkerを作って時間のかかる処理を別スレッドでやってる。
RunWorkerCompletedイベントでテキストブロックに結果を表示してる。このイベントは、RunWorkerAsyncとか呼び出したスレッドと同じスレッドで実行されるので安心してコントロールの値を変更できる。
using System.ComponentModel;
using System.Threading;
using System.Windows;
namespace WpfThread
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += (s, evt) =>
{
Thread.Sleep(10000);
evt.Result = "Complete!!";
};
bw.RunWorkerCompleted += (s, evt) =>
{
textBlock.Text = evt.Result.ToString();
};
textBlock.Text = "Start!!";
bw.RunWorkerAsync();
}
}
}
これを実行すると下のような感じ。
起動直後
ボタンを押した後
処理が終わったあと
画像じゃぜんぜん伝わらないけど、裏でスリープしてる間もウィンドウは固まることがない。
ということで、時間のかかる処理はWindowsFormsと同じ方法でやってしまおうということでした。