我试图用Caliburn.Micro.ReactiveUI编写一个非常简单的示例,这个示例将Caliburn.Micro文档中的基本配置、操作和约定示例和ReactiveUI.Sample.Commands样品结合在一起。我遇到的问题是,当调用任何命令时,因此对于DisplayCommand和StartAsyncCommand,都会抛出一个InvalidOperationException,说明调用的线程无法访问该对象,因为另一个线程拥有它。下面我已经包含了视图和ViewModel的代码,整个示例都在GitHub上,代码现在包含了Paul建议的修复程序。
搜索互联网并没有提供任何信息。我想我错过了一些显而易见的东西,任何帮助都是非常感谢的。
视图模型:
public class ShellViewModel : ReactivePropertyChangedBase, IShell
{
private string personName;
public ShellViewModel(IMessageBoxService messageBoxService)
{
DisplayCommand = new ReactiveCommand(this.WhenAny(x => x.PersonName, x => !string.IsNullOrEmpty(x.Value)));
DisplayCommand.Subscribe(_ => messageBoxService.ShowMessageBox("You clicked on DisplayCommand: Name is " + PersonName));
var localProgress = new Subject<int>();
localProgress.ToProperty(this, x => x.Progress, out progress);
StartAsyncCommand = new ReactiveCommand();
StartAsyncCommand.RegisterAsyncAction(_ =>
{
var currentProgress = 0;
localProgress.OnNext(currentProgress);
while (currentProgress <= 100)
{
localProgress.OnNext(currentProgress += 10);
Thread.Sleep(100);
}
});
}
public IReactiveCommand DisplayCommand { get; protected set; }
public string PersonName
{
get { return personName; }
set
{
this.RaiseAndSetIfChanged(ref personName, value);
}
}
private ObservableAsPropertyHelper<int> progress;
public int Progress
{
get { return progress.Value; }
}
public IReactiveCommand StartAsyncCommand { get; protected set; }
}意见:
<Window x:Class="CaliburnMicroReactiveUI.Sample.Commands.Views.ShellView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<Expander
IsExpanded="True"
Header="Simple Command">
<StackPanel>
<TextBox
Text="{Binding PersonName, UpdateSourceTrigger=PropertyChanged}" />
<Button
Command="{Binding DisplayCommand}"
Content="Display" />
</StackPanel>
</Expander>
<Expander
IsExpanded="True"
Header="Async Command">
<StackPanel>
<Button Command="{Binding StartAsyncCommand}" Content="Start" />
<ProgressBar Value="{Binding Progress, Mode=OneWay}" Height="20"/>
</StackPanel>
</Expander>
</StackPanel>
</Window>显示按钮生成以下堆栈跟踪:
System.Windows.Threading.Dispatcher.VerifyAccess()
System.Windows.DependencyObject.GetValue(DependencyProperty dp)
System.Windows.Controls.Primitives.ButtonBase.get_Command()
System.Windows.Controls.Primitives.ButtonBase.UpdateCanExecute()
System.Windows.Controls.Primitives.ButtonBase.OnCanExecuteChanged(Object sender, EventArgs e)
System.Windows.Input.CanExecuteChangedEventManager.HandlerSink.OnCanExecuteChanged(Object sender, EventArgs e)
ReactiveUI.ReactiveCommand.raiseCanExecuteChanged(EventArgs e)
ReactiveUI.ReactiveCommand.<.ctor>b__5()
System.Reactive.Concurrency.Scheduler.Invoke(IScheduler scheduler, Action action)
System.Reactive.Concurrency.DefaultScheduler.<>c__DisplayClass1`1.<Schedule>b__0(Object _)
System.Reactive.Concurrency.ConcurrencyAbstractionLayerImpl.<>c__DisplayClass1.<QueueUserWorkItem>b__0(Object _)
System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(Object state)
System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
System.Threading.ThreadPoolWorkQueue.Dispatch()
System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()“开始”按钮创建以下堆栈跟踪:
System.Windows.Threading.Dispatcher.VerifyAccess()
System.Windows.DependencyObject.GetValue(DependencyProperty dp)
System.Windows.Controls.Primitives.ButtonBase.get_Command()
System.Windows.Controls.Primitives.ButtonBase.UpdateCanExecute()
System.Windows.Controls.Primitives.ButtonBase.OnCanExecuteChanged(Object sender, EventArgs e)
System.Windows.Input.CanExecuteChangedEventManager.HandlerSink.OnCanExecuteChanged(Object sender, EventArgs e)
ReactiveUI.ReactiveCommand.raiseCanExecuteChanged(EventArgs e)
ReactiveUI.ReactiveCommand.<.ctor>b__5()
System.Reactive.Concurrency.Scheduler.Invoke(IScheduler scheduler, Action action)
System.Reactive.Concurrency.DefaultScheduler.<>c__DisplayClass1`1.<Schedule>b__0(Object _)
System.Reactive.Concurrency.ConcurrencyAbstractionLayerImpl.<>c__DisplayClass1.<QueueUserWorkItem>b__0(Object _)
System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(Object state)
System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
System.Threading.ThreadPoolWorkQueue.Dispatch()
System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()发布于 2014-01-06 18:41:59
我看了一下您的GitHub存储库,它看起来像是您刚刚添加了包Caliburn.Micro.ReactiveUI。这意味着您只有ReactiveUI核心,而且缺少平台特定的扩展。如果添加包反应性-xaml,一切都会更好(您仍然需要初始化该messageBoxService!)。
发布于 2013-12-19 21:26:19
您的问题是您正在更改您的Progress中的RegisterAsyncAction,它显式地保证它不会在UI线程上运行。这里有一个更好的方法,你可以这样做:
ObservableAsPropertyHelper<int> _progress;
public int Progress
{
get { return _progress.Value; }
}
public ShellViewModel(IMessageBoxService messageBoxService)
{
DisplayCommand = new ReactiveCommand(this.WhenAny(x => x.PersonName, x => !string.IsNullOrEmpty(x.Value)));
DisplayCommand.Subscribe(_ => messageBoxService.ShowMessageBox("You clicked on DisplayCommand: Name is " + PersonName));
var progress = new Subject<int>();
progress.ToProperty(this, x => x.Progress, out _progress);
StartAsyncCommand = new ReactiveCommand();
StartAsyncCommand.RegisterAsyncAction(_ =>
{
var currentProgress = 0;
progress.OnNext(currentProgress);
while (currentProgress <= 100)
{
progress.OnNext(currentProgress += 10);
Thread.Sleep(100);
}
});
}在本例中,您使用的是ToProperty的保证,即它为您处理对UI线程的封送处理
https://stackoverflow.com/questions/20687133
复制相似问题