Какой лучший способ обновить ObservableCollection из другого потока? - PullRequest
32 голосов
/ 30 марта 2011

Я использую BackgroundWorker для обновления ObservableCollection, но выдает эту ошибку:

"Этот тип CollectionView делает не поддерживает изменения в его SourceCollection из потока отличается от темы Dispatcher. "

Какой самый лучший и самый элегантный способ решить эту проблему с наименьшим количеством работы. Я не хочу писать многопоточный код на основе блокировок низкого уровня.

Я видел некоторые решения в Интернете, но им уже несколько лет, поэтому не уверен, каков последний консенсус в отношении решения этой проблемы.

Ответы [ 5 ]

36 голосов
/ 30 августа 2011

Если вы инициализируете коллекцию в конструкторе, она будет в потоке приложения по умолчанию.

Чтобы вызвать основной поток, вы можете сделать это:

Application.Current.Dispatcher.Invoke((Action)(() =>
    {
       //Do something here.
    }));

Вы должны разыграть анонимного делегата как действие, иначе он запутается ¯ \ O_o / ¯

Если вы используете Async CTP, вы можете сделать это

Application.Current.Dispatcher.InvokeAsync(()=>
    {
       //Do something here.
    });
12 голосов
/ 30 марта 2011

Если MVVM

public class MainWindowViewModel : ViewModel {

    private ICommand loadcommand;
    public ICommand LoadCommand { get { return loadcommand ?? (loadcommand = new RelayCommand(param => Load())); } }

    private ObservableCollection<ViewModel> items;
    public ObservableCollection<ViewModel> Items {
        get {
            if (items == null) {
                items = new ObservableCollection<ViewModel>();
            }
            return items;
        }
    }

    public void Load() {
        BackgroundWorker bgworker = new BackgroundWorker();
        bgworker.WorkerReportsProgress = true;
        bgworker.DoWork += (s, e) => {
            for(int i=0; i<10; i++) {
                System.Threading.Thread.Sleep(1000);
                bgworker.ReportProgress(i, new List<ViewModel>());
            }
            e.Result = null;
        };
        bgworker.ProgressChanged += (s, e) => {
            List<ViewModel> partialresult = (List<ViewModel>)e.UserState;
            partialresult.ForEach(i => {
                Items.Add(i);
            });
        };
        bgworker.RunWorkerCompleted += (s, e) => {
            //do anything here
        };
        bgworker.RunWorkerAsync();
    }
}
4 голосов
/ 30 марта 2011

Вы используете BGW, он был разработан, чтобы решить вашу проблему.Но вам придется использовать его правильно, обновите коллекцию в обработчике событий ProgressChanged или RunWorkerCompleted.Если это то, что вы делаете, то вы создали экземпляр BGW не в том потоке.Это должно быть сделано в потоке пользовательского интерфейса.

3 голосов
/ 14 марта 2013

Попробуйте:

this.Dispatcher.Invoke(DispatcherPriority.Background, new Action(
() =>
{

 //Code

}));
1 голос
/ 22 октября 2012

У меня была такая же проблема при перезагрузке observableCollection из события (на DataReceived), вызванного классом последовательного порта.Я использовал MVVM;Я попытался обновить коллекцию с помощью BackgroundWorker, но он поднял сообщение «Этот тип CollectionView не поддерживает изменения в его SourceCollection из потока, отличного от потока Dispatcher».Я пробовал многие другие решения, найденные в Интернете, но единственное, что решило мою проблему (немедленно!), Было использование многопоточной наблюдаемой коллекции (я использовал одно в этом посте: Где я могу получить потокобезопасный CollectionView)

...