Reputation: 1004
I've been trying to get a handle on the Rx library and working it out in WPF with MVVM. I broken my application into components such as repository and the ViewModel. My repository in able to provide a Collection of Students one by one but the moment i try to add into the View bound ObservableCollection it throws a thread error. I 'd apreciate some pointer on to get this working for me.
Upvotes: 7
Views: 8009
Reputation: 42991
You need to set the synchronization context correctly using
ObserveOn(SynchronizationContext.Current)
See this blog post
for an example.
Here's an example that works for me:
<Page.Resources>
<ViewModel:ReactiveListViewModel x:Key="model"/>
</Page.Resources>
<Grid DataContext="{StaticResource model}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Button Content="Start" Command="{Binding StartCommand}"/>
<ListBox ItemsSource="{Binding Items}" Grid.Row="1"/>
</Grid>
public class ReactiveListViewModel : ViewModelBase
{
public ReactiveListViewModel()
{
Items = new ObservableCollection<long>();
StartCommand = new RelayCommand(Start);
}
public ICommand StartCommand { get; private set; }
private void Start()
{
var observable = Observable.Interval(TimeSpan.FromSeconds(1));
//Exception: This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.
//observable.Subscribe(num => Items.Add(num));
// Works fine
observable.ObserveOn(SynchronizationContext.Current).Subscribe(num => Items.Add(num));
// Works fine
//observable.ObserveOnDispatcher().Subscribe(num => Items.Add(num));
}
public ObservableCollection<long> Items { get; private set; }
}
Upvotes: 8
Reputation: 2364
Any change to the UI should be done by the Dispatcher
thread. A good practice if you have anthoer thread constantly changing the view model is to force property setters to use the dispatcher thread. In this case you make sure that you wont change a UI element on another thread.
Try:
public string Property
{
set
{
Dispatcher.BeginInvoke(()=> _property = value ) ;
OnPropertyChanged("Property");
}
get
{
return _property;
}
}
Upvotes: 1
Reputation: 2368
Is your code running on a background thread? As it affects the UI, a View bound ObservableCollection can only be updated on the UI/Dispatcher thread.
See WPF ObservableCollection Thread Safety for a similar issue.
Upvotes: 1