0

ReactiveUIを使用してWindowsPhone7.1用の最初のアプリを開発していますが、ReactiveCollectionクラスの操作で問題が発生しています。

私のアプリは、おおよそWP7 SQL CEエンジン(LINQ to SQL)へのアクセスに関するものです。ReactiveAsyncCommandを使用して、バックグラウンドでデータ操作を実行したいと思います。データベースからのデータは「自動的に」UIに表示されるはずなので、データベースとUIの間のプロキシとしてReactiveCollectionを使用することにしました。

これが私のモデルなので、もっと良いアイデアがあります。

public class EncounteredModel
{
    public ReactiveCollection<Fact> FactsCollection;

    public ReactiveCollection<FactEntry> FactEntriesCollection;

    private EncounteredModel()
    {
        using (EncounteredDataContext db = new EncounteredDataContext())
        {
            FactsCollection = new ReactiveCollection<Fact>(from fact in db.Facts select fact);
            FactEntriesCollection = new ReactiveCollection<FactEntry>(from factEntry in db.FactEntries select factEntry);
        }

        FactsCollection.ItemsAdded.Subscribe(fact => { using (var db = new EncounteredDataContext()) { db.Facts.InsertOnSubmit(fact); db.SubmitChanges(); } });
        FactsCollection.ItemsRemoved.Subscribe(fact => { using (var db = new EncounteredDataContext()) { db.Facts.DeleteOnSubmit(fact); db.SubmitChanges(); } });

        FactEntriesCollection.ItemsAdded.Subscribe(factEntry => { using (var db = new EncounteredDataContext()) { db.FactEntries.InsertOnSubmit(factEntry); db.SubmitChanges(); } });
        FactEntriesCollection.ItemsRemoved.Subscribe(factEntry => { using (var db = new EncounteredDataContext()) { db.FactEntries.DeleteOnSubmit(factEntry); db.SubmitChanges(); } });
    }

    private static EncounteredModel instance;

    public static EncounteredModel Instance
    {
        get 
        {
            if (instance == null)
                instance = new EncounteredModel();
            return instance; 
        }
    }

}

私のビューモデルでは、2つの異なるバリアントを使用してみました。

  1. 派生したリアクティブコレクションを作成し、UIをそれにバインドします(ReactiveCollection.CreateDerivedCollection()メソッドを使用します。たとえば、EncounteredModel.FactsCollectionから派生します。
  2. を使用ObservableAsPropertyHelper<IEnumerable<Fact>>してから、モデルのReactiveCollection ChangedIObservableにサブスクライブして、OAPHにデータを入力します。

残念ながら、どちらのバリアントも「無効なクロススレッドアクセス」を提供します。スタックトレースは、通常、両方のバリアントで同じです。これは、バリアント1の1つです(重要な部分に短縮されています)。

   at MS.Internal.XcpImports.CheckThread()
   at System.Windows.DependencyObject.GetValueInternal(DependencyProperty dp)
   at System.Windows.FrameworkElement.GetValueInternal(DependencyProperty dp)
   at System.Windows.DependencyObject.GetValue(DependencyProperty dp)
   at System.Windows.Controls.ItemsControl.get_ItemsHost()
   at System.Windows.Controls.ItemsControl.OnItemsChangedHandler(Object sender, ItemsChangedEventArgs args)
   at System.Windows.Controls.ItemContainerGenerator.OnItemAdded(Object item, Int32 index, Boolean suppressEvent)
   at System.Windows.Controls.ItemContainerGenerator.System.Windows.Controls.ICollectionChangedListener.OnCollectionChanged(Object sender, NotifyCollectionChangedEventArgs args)
   at System.Windows.Controls.WeakCollectionChangedListener.SourceCollectionChanged(Object sender, NotifyCollectionChangedEventArgs e)
   at System.Windows.Controls.ItemCollection.NotifyCollectionChanged(NotifyCollectionChangedEventArgs e)

ReactiveCommand(非同期コマンドではない)に変更すると、すべて問題ありません。これを克服する方法はありますか?よろしくお願いします。

4

1 に答える 1

0

ReactiveCollection は、すべてを UI スレッドにプロキシするわけではありません。ワーカー スレッドから項目を追加すると、同じスレッドで UI にシグナルが送られ、クラッシュが発生します。

ただし、ReactiveAsyncCommand が行うことの 1 つは、UI スレッドで結果を返すことです。そのため、次のようなことができます。

var cmd = new ReactiveAsyncCommand();
cmd.RegisterAsyncFunc(() => getAllTheItems())
   .Subscribe(theItems => theItems.ForEach(item => collection.Add(item)));

Subscribe、UI スレッド上にあることが保証されています (そうでない場合、それはバグです)。

于 2012-09-15T05:57:27.277 に答える