読者です 読者をやめる 読者になる 読者になる

Silverlightで非同期にObservableCollectionを操作しようとしてハマった

はじめに

Silverlight では、コントロールにバインドされた ObservableCollection を、非同期に操作したいケースがよくあります。
例えば、サービス呼出しの結果を反映させたいときとか。Silverlight の通信は非同期が基本なので。

MSDN マガジンに参考記事があります!

非同期通信を行い、処理結果を ObservableCollection に反映させるサンプルが MSDN マガジンにあります。

Silverlight アプリケーションを MVVM で作成する記事。Silverlight2 のときの記事だけど、Silverlight3 でも使えるハズ。

ところが…

この記事を参考に、非同期で取得したデータを ObservableCollection に追加するために

Application.Current.RootVisual.Dispatcher.BeginInvoke(() =>
{
    // ObservableCollection の操作
    this.Items.Add(data);
});

と記述したら、例外が発生。
デバッガで追ってみたところ、RootVisual プロパティにアクセスした時点で例外が発生してました。
UI スレッド外から RootVisual プロパティにアクセスした時点でアウト!

この問題を回避する方法

非同期処理を開始する前に、Dispatcher への参照を保存しておけばいいです。

// Dispatcher 保存
Dispatcher rootDispatcher = Application.Current.RootVisual.Dispatcher;

// 非同期処理スタート
WebRequest request = WebRequest.Create(WEB_API_URL);
request.BeginGetResponse(result =>
{
    WebResponse response = request.EndGetResponse(result);
    using(Stream stream = response.GetResponseStream())
    {
        XmlSerializer serializer = new XmlSerializer(typeof(Foo));
        Foo data = (Foo)serializer.Deserialize(stream);

        // UI スレッド上で ObservableColelction を操作する。
        // Items は ObservableCollection<Foo> 型。
        // 保存しておいた Dispatcher を使用。
        rootDispatcher.BeginInvoke(() =>
        {
            this.Items.Add(data);
        });
    }
}, request);

RootVisual の Dispatcher は、スタティック変数に代入しておいて、プロジェクト全体で使い回せばいいと思います。

MSDN マガジンの記事にまんまとやられました

MSDN マガジンのコードは間違っていない、自分のコードがどこか間違っているはず」と思いこんで、デバッガで逐次実行しなかったのも問題ですけど……。

2日ほど潰しましたorz