ReactiveUI调用线程无法访问此对象,因为另一个线程拥有它

时间:2013-05-16 17:59:14

标签: wpf reactiveui

如果我在代码中使用绑定,请在点击更改IsBusy

后收到错误
"The calling thread cannot access this object because a different thread owns it"

XAML:

<Button x:Name="AsyncCommand"
                    Height="20"
                    Content="PushAsync"/>
<ProgressBar x:Name="IsBusy"
              Height="20"/>

CS:

this.Bind(ViewModel, x => x.IsBusy, x => x.IsBusy.IsIndeterminate);
this.BindCommand(ViewModel, x => x.AsyncCommand, x => x.AsyncCommand);

视图模型:

public class TestViewModel : ReactiveObject
    {
        public TestViewModel()
        {
            AsyncCommand = new ReactiveAsyncCommand();
            AsyncCommand
                .RegisterAsyncFunction(x => 
                 { IsBusy = true; Thread.Sleep(3000); return "Ok"; })
                .Subscribe(x => { IsBusy = false; });
        }

        private bool isBusy;

        public bool IsBusy
        {
            get { return isBusy; }
            set { this.RaiseAndSetIfChanged(x => x.IsBusy, ref isBusy, value); }
        }
        public ReactiveAsyncCommand AsyncCommand { get; protected set; }
    }

但是如果我在xaml中进行绑定都可以,就像这样:

CS:

DataContext = new TestViewModel();

XAML:

<Button x:Name="AsyncCommand"
                    Height="20"
                    Content="PushAsync"
                    Command="{Binding AsyncCommand}"/>
<ProgressBar x:Name="IsBusy"
              Height="20"
              IsIndeterminate="{Binding IsBusy}"/>

为什么会这样?

2 个答案:

答案 0 :(得分:1)

试试这个:

public TestViewModel()
{
    AsyncCommand = new ReactiveAsyncCommand();
    AsyncCommand.Subscribe(_ => IsBusy = true);

    AsyncCommand
        .RegisterAsyncFunction(x => 
         { Thread.Sleep(3000); return "Ok"; })
        .Subscribe(x => { IsBusy = false; });
}

甚至更好:

ObservableAsPropertyHelper<bool> _IsBusy;
public bool IsBusy {
    get { return _IsBusy.Value; }
}

public TestViewModel()
{
    AsyncCommand = new ReactiveAsyncCommand();
    AsyncCommand
        .RegisterAsyncFunction(x => 
         { Thread.Sleep(3000); return "Ok"; })
        .Subscribe(x => { /* do a thing */ });

    AsyncCommand.ItemsInFlight
        .Select(x => x > 0 ? true : false)
        .ToProperty(this, x => x.IsBusy);
}

答案 1 :(得分:0)

我假设您的ViewModel属性的实现与此类似:

public TestViewModel ViewModel
{
    get { return (TestViewModel)DataContext; }
    set { DataContext = value; }
}

在这种情况下,当您单击按钮时,在非UI线程上调用RegisterAsyncFunction中的lambda函数。在IsBusy = false指令中,ReactiveUI调用ViewModel属性,该属性尝试在非UI线程上获取DataContext,从而导致InvalidOperationException

如果将ViewModel绑定到Xaml中的View,则不会调用ViewModel属性。

要修复此代码,您应使用Dispatcher.Invoke来致电IsBusy = false

AsyncCommand
    .RegisterAsyncFunction(x => 
    {
        Application.Current.Dispatcher.Invoke(() =>IsBusy = true); 
        Thread.Sleep(3000); 
        return "Ok"; 
    })'