Reputation: 23
If I use a binding in code behind, getting an error after click at change 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);
viewmodel:
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; }
}
But if I make a bind in xaml all works, like this:
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}"/>
Why is this happening?
Upvotes: 2
Views: 2361
Reputation: 74654
Try this:
public TestViewModel()
{
AsyncCommand = new ReactiveAsyncCommand();
AsyncCommand.Subscribe(_ => IsBusy = true);
AsyncCommand
.RegisterAsyncFunction(x =>
{ Thread.Sleep(3000); return "Ok"; })
.Subscribe(x => { IsBusy = false; });
}
Or even better:
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);
}
Upvotes: 2
Reputation: 189
I assume that your ViewModel property is implemented similiar to this:
public TestViewModel ViewModel
{
get { return (TestViewModel)DataContext; }
set { DataContext = value; }
}
In that case, when you click button, lambda function from your RegisterAsyncFunction
is called on non-UI thread. In IsBusy = false
instruction ReactiveUI invokes ViewModel property which tries to get DataContext on non-UI thread, which causes InvalidOperationException
.
If you bind your ViewModel to View in Xaml, then ViewModel property isn't called.
To fix this code you should use Dispatcher.Invoke to call IsBusy = false
:
AsyncCommand
.RegisterAsyncFunction(x =>
{
Application.Current.Dispatcher.Invoke(() =>IsBusy = true);
Thread.Sleep(3000);
return "Ok";
})'
Upvotes: 0