2

如果我在后面的代码中使用绑定,单击更改 IsBusy 后会出现错误

"The calling thread cannot access this object because a different thread owns it"

xml:

<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();

xml:

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

为什么会这样?

4

2 回答 2

2

试试这个:

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);
}
于 2013-05-16T20:40:21.693 回答
0

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

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

在这种情况下,当您单击按钮时,您的 lambda 函数RegisterAsyncFunction会在非 UI 线程上调用。在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"; 
    })'
于 2013-05-16T19:56:02.553 回答