1

ReactiveUI を勉強するための簡単な WP7 アプリを書いています。画面にコレクションを表示したい、アイテムを選択可能にする必要があり、選択したすべてのアイテムをコレクションから削除するコマンドが必要です。また、コマンドは、選択された項目が少なくとも 1 つある場合にのみ実行可能でなければなりません。私はこのように定義されたコレクションを持っています:

Persons = model.Persons
                .CreateDerivedCollection(x => new PersonViewModel(x));

私はPersonViewModelプロパティを持っています:

private bool _isSelected;
public bool IsSelected
{
    get { return _isSelected; }
    set { this.RaiseAndSetIfChanged(x => x.IsSelected, ref _isSelected, value); }
}

モデルの選択状態に関する情報はなく、viewModel だけです。Page ViewModel には、次のコードがあります。

Persons = model.Persons.CreateDerivedCollection(x => new PersonViewModel(x));
Persons.ChangeTrackingEnabled = true;

var deleteSelectedCanExecute = Persons.ItemChanged
                                  .Select(_ => Persons .Any(p => p.IsSelected));

DeleteSelectedCommand = new ReactiveCommand
                (
                       deleteSelectedCanExecute
                );
DeleteSelectedCommand.Subscribe(
                x => RemoveSelected()
                );

そして方法:

   private void RemoveSelected()
    {
        var res = Persons.Where(p => p.IsSelected)
             .Select(x => x.Model).ToList();
        foreach (var person in res)
        {
            _model.Persons.Remove(person);
        }
    }

最初の質問 (それほど重要ではありません。自分で解決策を見つけることができると思います): アプリを実行すると、DeleteSelected ボタンが Active.DeleteSelectedCommand.CanExecute になりません。ただし、アイテムを選択/選択解除した後、ボタンの状態は正常に機能します。

そして主な問題:

DeleteSelectedCommand を実行すると、選択したすべての項目が削除されます (デバッガーに表示されます)。そして、次のスタック トレースで「NotSupportedException」を取得しました。

   at System.Threading.Interlocked.Decrement(Int64& location)
   at ReactiveUI.RefcountDisposeWrapper.Release()
   at ReactiveUI.ReactiveCollection`1.removeItemFromPropertyTracking(PersonViewModel toUntrack)
   at ReactiveUI.ReactiveCollection`1.<setupRx>b__18(PersonViewModelx)
   at System.Reactive.AnonymousObserver`1.Next(PersonViewModelvalue)
   at System.Reactive.AbstractObserver`1.OnNext(PersonViewModelvalue)
   at System.Reactive.AutoDetachObserver`1.Next(PersonViewModelvalue)
   at System.Reactive.AbstractObserver`1.OnNext(PersonViewModelvalue)
   at System.Reactive.ScheduledObserver`1.<>c__DisplayClass4.<Next>b__2()
   at System.Reactive.ScheduledObserver`1.<EnsureActive>b__0(Action self)
   at System.Reactive.Concurrency.Scheduler.<Schedule>b__0(Action`1 _action, Action`1 self)
   at System.Reactive.Concurrency.Scheduler.<>c__DisplayClass9`1.<InvokeRec1>b__6(Action`1 state1)
   at System.Reactive.Concurrency.Scheduler.InvokeRec1[TState](IScheduler scheduler, Pair`2 pair)
   at System.Reactive.Concurrency.DispatcherScheduler.<>c__DisplayClass1`1.<Schedule>b__0()
   at System.Reflection.RuntimeMethodInfo.InternalInvoke(RuntimeMethodInfo rtmi, Object obj, BindingFlags invokeAttr, Binder binder, Object parameters, CultureInfo culture, Boolean isBinderDefault, Assembly caller, Boolean verifyAccess, StackCrawlMark& stackMark)
   at System.Reflection.RuntimeMethodInfo.InternalInvoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, StackCrawlMark& stackMark)
   at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
   at System.Delegate.DynamicInvokeOne(Object[] args)
   at System.MulticastDelegate.DynamicInvokeImpl(Object[] args)
   at System.Delegate.DynamicInvoke(Object[] args)
   at System.Windows.Threading.DispatcherOperation.Invoke()
   at System.Windows.Threading.Dispatcher.Dispatch(DispatcherPriority priority)
   at System.Windows.Threading.Dispatcher.OnInvoke(Object context)
   at System.Windows.Hosting.CallbackCookie.Invoke(Object[] args)
   at System.Windows.Hosting.DelegateWrapper.InternalInvoke(Object[] args)
   at System.Windows.RuntimeHost.ManagedHost.InvokeDelegate(IntPtr pHandle, Int32 nParamCount, ScriptParam[] pParams, ScriptParam& pResult)

だから私はそれを間違っていますが、何が問題なのですか?STからはわかりません。この動作を実装する正しい方法は何ですか。それはとても一般的ですよね?

更新

deleteSelectedCanExecute に関するすべてのコードを削除してプログラムを実行すると、クラッシュします。削除するParticipants.ChangeTrackingEnabled = true;と、期待どおりに機能します。

4

1 に答える 1

2

Anton さん、あなたは新しいReactiveUI コミットの所有者です。ソースからビルドすれば、クラッシュは解消するはずです。

選択に関する質問ですが、これは、コレクションのサイズが変わる可能性がある場合、現時点で実行するのが少し難しいシナリオの 1 つです。どこかのコードは、コレクション内の各アイテムを購読する必要があるだけでなく、追加または削除されるアイテムのリストも保持する必要があります (つまり、選択されなくなるには、=>から移動するか、削除されるという2 つの方法があります)。Item.IsSelectedtruefalse Item

急速に変化するリストがない場合は、やや非効率的ですが、はるかに簡単な方法でこれを行うことができます。

var cmd = new ReactiveCommand(
    Persons.ItemsCountChanged.Select(_ => 
        Persons.Any(x => x.IsSelected)));

ちなみに、このソリューションも を必要としないためChangeTrackingEnabled、修正したばかりのバグを回避する必要はありません。

于 2012-07-29T17:02:52.977 に答える