首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用Caliburn.Micro.ReactiveUI 1.2.2时不正确的线程访问UI对象

使用Caliburn.Micro.ReactiveUI 1.2.2时不正确的线程访问UI对象
EN

Stack Overflow用户
提问于 2013-12-19 16:44:07
回答 2查看 711关注 0票数 1

我试图用Caliburn.Micro.ReactiveUI编写一个非常简单的示例,这个示例将Caliburn.Micro文档中的基本配置、操作和约定示例ReactiveUI.Sample.Commands样品结合在一起。我遇到的问题是,当调用任何命令时,因此对于DisplayCommandStartAsyncCommand,都会抛出一个InvalidOperationException,说明调用的线程无法访问该对象,因为另一个线程拥有它。下面我已经包含了视图和ViewModel的代码,整个示例都在GitHub上,代码现在包含了Paul建议的修复程序。

搜索互联网并没有提供任何信息。我想我错过了一些显而易见的东西,任何帮助都是非常感谢的。

视图模型:

代码语言:javascript
复制
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; }
}

意见:

代码语言:javascript
复制
<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>

显示按钮生成以下堆栈跟踪:

代码语言:javascript
复制
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()

“开始”按钮创建以下堆栈跟踪:

代码语言:javascript
复制
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()
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2014-01-06 18:41:59

我看了一下您的GitHub存储库,它看起来像是您刚刚添加了包Caliburn.Micro.ReactiveUI。这意味着您只有ReactiveUI核心,而且缺少平台特定的扩展。如果添加包反应性-xaml,一切都会更好(您仍然需要初始化该messageBoxService!)。

票数 3
EN

Stack Overflow用户

发布于 2013-12-19 21:26:19

您的问题是您正在更改您的Progress中的RegisterAsyncAction,它显式地保证它不会在UI线程上运行。这里有一个更好的方法,你可以这样做:

代码语言:javascript
复制
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线程的封送处理

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/20687133

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档