3

Хорошо, после вчерашнего дня я добавил новый уровень сложности. У нас все еще есть теоретический класс Model, ViewModel и View. На этот раз моя модель имеет Threading.Timer (Chosen специально, чтобы получить обратные вызовы таймера на «неправильной» нити.INotifyPropertyChanged, ObservableCollection, Threads и MVVM

Модель имеет ObservableCollection. Таймер обратного вызова добавляет элементы в коллекции.

ViewModel просто передает сбор в View, который содержит ListBox привязан к коллекции.

Это не работает.

модель также предоставляет строку, которая обновляется в том же обратном вызове таймера.

Это тоже отображается через viewmodel и привязывается к TextBox.

Это действительно работает.

Я видел подсказки в моем поиске в Google, что обновление коллекций не делает работу INotifyCollectionChanged, как ожидалось. Я получаю полную имплозию, даже не исключение, только немедленное прекращение действия приложения.

Итак, есть два вопроса:

Один относится к нашей дискуссии вчера. Я использую INotifyPropertyChanged и ObservableCollections в своей модели, потому что это то, на что работает представление. Мне все же имеет смысл использовать эти механизмы для уведомления моей модели просмотра или того, что когда-либо изменила базовая модель. Итак, как мне обрабатывать обновления, происходящие в другом потоке?

Во-вторых, что происходит, когда INotifyPropertyChanged работает со связыванием? Я привязываю свойство string к DependencyProperty, называемому Text, так же как и система DependencyProperty, которая возвращает мои изменения обратно в поток пользовательского интерфейса? Edit: И я могу полагаться на это, то есть делает это, потому что они ожидают, что я буду говорить с ним поперечным потоком, или это просто уловка, на которую я не должен полагаться?

ListBox связан через ItemsSource = "{Binding ObsCollection}". Когда это приведет к сбою приложения. На самом деле, я сначала начал таймер, когда была создана модель, которая произошла, когда DataContext окна был установлен, так что это будет на самом деле бомбой Visual Studio ...

Благодаря

ответ

1

управления WPF имеет нить сродство, что это означает, что их свойства могут быть изменены только из потока пользовательского интерфейса. Поэтому, если вы обновляете значение свойства из таймера (кроме DispatcherTimer), вам придется маршевать это обновление в потоке пользовательского интерфейса. Это проведенное через диспетчер:

Application.Current.Dispatcher.BeginInvoke(
    DispatcherPriority.Normal, 
    new Action(() => // update your control here)); 

рамка привязки данных не гарантирует, что обновления ранжированы на поток пользовательского интерфейса, поэтому, если вы обновите вашу модель из другого потока, это вызовет проблемы. поэтому вам нужно использовать тот же шаблон, что и выше. Другими словами, если вы добавляете возражения в свою наблюдаемую коллекцию, это добавление должно выполняться через диспетчер.

+1

Это круто, но что такое «правильный» процесс для решения этой проблемы с точки зрения MVVM? Таким образом, поток моей модели обновляет коллекцию. Должна ли моя модель узнать о диспетчерах? Это кажется неправильным. Это говорит о том, что мой ViewModel более тяжелый, чтобы справиться с правильным разделением между Model и View. Имеет ли это смысл? – Ian

+1

Не существует «правильного» подхода. Тем не менее, мое личное предпочтение заключается в создании простого интерфейса, от которого зависит ViewModel, IMarshalledInvoker, который имеет единственный метод для вызова некоторых действий над каким-либо другим потоком. Когда я соединяю ViewModel с представлением, я создаю IMarshalledInvoker, который использует диспетчер под обложками. С этим шаблоном я все еще могу тестировать блок, у меня все еще есть поддержка дизайнера, т. Е. Это хорошая MVVM ;-) – ColinE

+0

А теперь все вместе. В сущности, существуют разрозненные знания, которые связаны друг с другом так, как я не рассматривал. Первая biggie, мы знаем, что сборка потокобезопасной коллекции сложна. Я забыл об этом и не сделал ассоциации в моей голове ... По существу, я добавляю только один поток. Забавно, как вы забываете, почему вы делаете что-то. Так что я думаю, что суть ответа заключается в том, чтобы вносить изменения из нескольких потоков в один поток. Итак, есть один канал между представлением и viewmodel ?? – Ian

2

Эта проблема довольно распространена в WPF. Я считаю, что лучшим вариантом является наличие собственного подкласса ObservableCollection<>, который будет автоматически обрабатывать уведомления о событиях в потоке пользовательского интерфейса.

И так как колесо уже было изобретено, я просто передам вам ответ на этот вопрос: ObservableCollection and threading.