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

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

.net

はじめに

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