我正在编写简单的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;
- 它可以正常工作。
答案 0 :(得分:2)
安东,你是一个新的ReactiveUI commit的自豪拥有者 - 从源头构建,你的崩溃应该消失。
关于选择的问题,如果集合可以改变大小,这是一个有点棘手的事情。代码某处不仅要订阅集合中的每个项,它还必须保留一个列表,列出要添加或删除的项目(即两个方法不再存在被选中,Item.IsSelected
来自true
=&gt; false
,或 Item
被删除。
如果您没有快速变化的列表,您可以通过更简单的方式执行此操作:
var cmd = new ReactiveCommand(
Persons.ItemsCountChanged.Select(_ =>
Persons.Any(x => x.IsSelected)));
顺便说一句,此解决方案也不需要ChangeTrackingEnabled
,因此您不需要解决我刚修复的错误。