1

MVVM パターンを使用して、Windows ストアと Windows Phone 8 の両方で同じアプリの 2 つのバージョンを開発しています。各アプリには独自のビューがあります。Model と ViewModel は Portable Class Libraray で共有されます。TPL タスクを使用して、モデル内で非同期操作を行っています。ポータブル クラス ライブラリの制限により、async キーワードと await キーワードを使用できません。

Task が終了したら、UI スレッドに戻っていくつかのプロパティを更新したいと考えています (これにより、ViewModel と View も更新されます)。

これは非常に一般的な状況のように思えるので、なぜそれほど難しいことが判明したのか、少し混乱しています。

私は2つの異なるアプローチを試みました:

1つ(機能しません)

操作を開始する前にスケジューラへの参照を保存します

TaskScheduler scheduler = TaskScheduler.FromCurrentSynchronizationContext();

それをContinueWithメソッドに渡します。

myTask.ContinueWith(t => myTaskCompleted(t.Result), scheduler);

これは良い解決策のように思えますが、うまくいきません。myTaskCompleted は引き続き別のスレッドで実行されます。

2番

今、私は使用しようとしました

Dispatcher.RunAsync(CoreDispatcherPriority.Normal, handler);

PCL から Dispatcher を直接使用することはできないため、(ラッパーに隠されている) Dispatcher への参照をモデル内のほぼすべてのオブジェクトに渡します。(この回答のように)これは最終的に機能しますが、非常に複雑で醜いです。

だから私の質問は:

  1. ポータブル クラス ライブラリ内の UI スレッドに戻るための推奨される方法はどれですか?
  2. 試行Oneでの私の間違いは何ですか?

このトピックにはすでに多くの質問があることは知っていますが、残念ながら私の問題を本当に解決するものは何もありません.

4

1 に答える 1

2

TPL はスレッド プールのスレッドを使用します。UI スレッドは「メイン スレッド」であり、スレッド プールにはなく、タスクを実行することはできません。ContinueWith関数を使用すると、スレッド プールから別のスレッドが取得され、コードが実行されます。あなたが直面している問題の核心は、Windows Phone がプロパティの変更をキューに入れず、ビューを直接更新しようとするという事実にあります。コードのどこかに、プロパティの変更をブロードキャストするChanged関数が必要です。私は私のものを使用します:

public void Changed(string Key) {
    // Check if the property changed has subscribers.
    if (PropertyChanged != null) {
        // Invoke the property changed.
        PropertyChanged(this, new PropertyChangedEventArgs(Key));
    }
}

WPF はプロパティの変更をキューに入れ、次の UI フレームの更新でそれらを処理するため、この変更された関数は WPF アプリケーションで正常に動作します。Windows Phone はそうではないため、実行時にこの動作を変更するパターンを確立する必要があります。Dispatcherというプロパティを作成し、実行時に設定できるようにしました。すべてのブロードキャストがChangedからDispatcherに変更されました。

private Action<string> _Dispatcher;
public Action<string> Dispatcher {
    get {
        if (_Dispatcher == null) {
            return Changed;
        }
        return _Dispatcher;
    }
    set {
        _Dispatcher = value;
    }
}

これで、Windows Phone アプリケーションで実行時にDispatcherを変更できるようになりました。UI スレッドがアクティブになって変更をブロードキャストするまで、変更を延期する関数を作成する必要があります。これを拡張機能で行ったので、ViewModel に UI スレッド セーフをアタッチする方が少し簡単です。実行時の変更は、Windows Phone Dispatcherを使用して、UI スレッドでブロードキャストをスケジュールするだけです。実装は次のとおりです。

public static void Attach(this ViewModelStore ViewModelStore, DependencyObject DependencyObject) {
    // Set the changed event dispatcher.
    ViewModelStore.Dispatcher = (Key) => {
        // Begin invoking of an action on the UI dispatcher.
        DependencyObject.Dispatcher.BeginInvoke(() => {
            // Raise the changed event.
            ViewModelStore.Changed(Key);
        });
    };
}

ViewModelStoreは、私がすべてのビュー モデルに使用してきたジェネリック クラスであるため、この関数を使用すると、スレッド セーフなブロードキャスト メカニズムをすべてのビュー モデルにアタッチできます。DependencyObjectは、ビューなどの UI コンポーネントです。あとは、ビュー モデルでattachを呼び出すだけです。

ProviderViewModel.Attach(this); // This is inside a Page.

すべてのブロードキャストが UI スレッドに委任されるわけではなく、UI が入ってくる次のフレームで呼び出され、それに応じてすべてが更新されます。このようなスレッド セーフについて心配する必要はありませんが、Windows Phone アプリケーションでビュー モデルの新しいインスタンスをアタッチすることを忘れないでください。さらに質問がある場合はお知らせください。幸運を祈ります。

于 2013-03-16T11:42:20.713 に答える