RxUI:从集合中删除项目后的异常

时间:2012-07-29 12:54:11

标签: windows-phone reactiveui

我正在编写简单的WP7应用程序来学习ReactiveUI。 我想在屏幕上显示一个集合,项目应该是可选择的,我想要一个Command,它从集合中删除所有选定的项目。此命令必须仅在至少有一个选定项目时才可执行。 我有一个以这种方式定义的集合:

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无法理解。什么是实现此行为的正确方法。它是如此常见,不是吗?

UPD

如果我删除有关deleteSelectedCanExecute的所有代码并运行程序 - 它会崩溃。如果我删除Participants.ChangeTrackingEnabled = true; - 它可以正常工作。

1 个答案:

答案 0 :(得分:2)

安东,你是一个新的ReactiveUI commit的自豪拥有者 - 从源头构建,你的崩溃应该消失。

关于选择的问题,如果集合可以改变大小,这是一个有点棘手的事情。代码某处不仅要订阅集合中的每个,它还必须保留一个列表,列出要添加或删除的项目(即两个方法不再存在被选中,Item.IsSelected来自true =&gt; false Item被删除。

如果您没有快速变化的列表,您可以通过更简单的方式执行此操作:

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

顺便说一句,此解决方案也不需要ChangeTrackingEnabled,因此您不需要解决我刚修复的错误。