1

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

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 提交的骄傲拥有者- 从源代码构建,你的崩溃应该消失。

至于你关于选择的问题,如果集合可以改变大小,这是一个目前有点棘手的场景。某处的代码不仅必须订阅集合中的每个项目,还必须保留正在添加或删除哪些项目的列表(即有两种不再选择的方法,Item.IsSelectedtrue=>开始false或被 Item删除)。

如果您没有快速更改的列表,则可以以一种效率较低但更简单的方式执行此操作:

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

顺便说一句,这个解决方案也不需要ChangeTrackingEnabled,所以你不需要解决我刚刚修复的错误。

于 2012-07-29T17:02:52.977 回答