Обновление ObservableCollection из другого потока - PullRequest
6 голосов
/ 20 февраля 2012

Я пытался разобраться с библиотекой Rx и отрабатывал ее в WPF с MVVM. Я разбил свое приложение на такие компоненты, как репозиторий и ViewModel. Мой репозиторий в состоянии предоставить Коллекцию Студентов один за другим, но в тот момент, когда я пытаюсь добавить в ObviewableCollection, связанную с View, он выдает ошибку потока. Я бы оценил какой-то указатель, чтобы это сработало для меня.

Ответы [ 3 ]

8 голосов
/ 20 февраля 2012

Вам необходимо правильно установить контекст синхронизации, используя

ObserveOn(SynchronizationContext.Current)

См. Этот блог

http://10rem.net/blog/2011/02/17/asynchronous-web-and-network-calls-on-the-client-in-wpf-and-silverlight-and-net-in-general

для примера.

Вот пример, который работает для меня:

<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; }
}
1 голос
/ 20 февраля 2012

Любые изменения в пользовательском интерфейсе должны выполняться потоком Dispatcher.Хорошей практикой, если у вас есть другой поток, постоянно меняющий модель представления, это заставлять установщики свойств использовать поток диспетчера.В этом случае вы убедитесь, что не измените элемент пользовательского интерфейса в другом потоке.

Попробуйте:

public string Property 
{ 
   set 
    { 
      Dispatcher.BeginInvoke(()=> _property = value ) ; 
      OnPropertyChanged("Property");  
    } 
   get 
    { 
      return _property; 
    }
}
1 голос
/ 20 февраля 2012

Ваш код работает в фоновом потоке? Поскольку это влияет на пользовательский интерфейс, коллекция ObservableCollection, связанная с представлением, может обновляться только в потоке пользовательского интерфейса / диспетчера.

См. WPF ObservableCollection Thread Safety для аналогичной проблемы.

...