WPF DependencyObject, вызывающий исключение потока

У меня есть следующий код, который создает временную папку и использует FileSystemWatcher для опроса файлов, добавленных в папку в свойстве Location, и добавляет их в список: Scratchdisk.cs на Pastebin. Идея состоит в том, чтобы создать объект Scratchdisk и заставить FFmpeg извлекать в него видеокадры, FileSystemWatcher создает список этих файлов по мере их создания FFmpeg, и этот список представляется как DependencyObject, к которому привязывается мой пользовательский интерфейс.

Я привязываюсь к объекту Scratchdisk следующим образом:

<ItemsControl ItemsSource="{Binding Source=ThumbnailScratchdisk, Path=FileList}">
...
</ItemsControl>

Однако при фактическом создании объекта я получаю следующее исключение:

A first chance exception of type 'System.InvalidOperationException' occurred in WindowsBase.dll

Additional information: The calling thread cannot access this object because a different thread owns it.

в строке 28 get { return (List<string>)GetValue(FileListProperty); }

Я думаю, что мне нужен Dispatcher.Invoke где-то, но я понятия не имею, где, я не знаю, где создается второй поток. Я предполагаю, что это как-то связано с записью FileSystemWatcher в список файлов.

Любая помощь?

Спасибо!


person Callum Booth    schedule 23.11.2014    source источник
comment
FileSystemWatcher вызывает события из другого потока. Попробуйте заменить Watcher_Created на что-то вроде this.Dispatcher.Invoke(new Action(() => AddFileReference(e.FullPath))).   -  person dkozl    schedule 23.11.2014
comment
@dkozl Это сработало, спасибо!   -  person Callum Booth    schedule 23.11.2014


Ответы (3)


Доступ к нему у меня такой. Он получает диспетчер потока пользовательского интерфейса

System.Windows.Application.Current.Dispatcher.Invoke(
  (Action)(() => 
  {
      //Access the UI from here 
  }));

Главное, что следует отметить между тем, что у меня есть, и тем, что вы перечислили в комментариях, это то, что мой будет работать независимо от того, находитесь ли вы в скрытом коде, модели представления, классе обслуживания и т. д. Не на всех предметах есть Dispatcher, поэтому this.Dispatcher не всегда работает.

person Anthony Russell    schedule 20.07.2016

Вы можете обернуть свой вызов внутри Action(), вызываемого из Dispatcher, следующим образом:

this.Dispatcher.BeginInvoke(new Action(() =>
{
    // your code accessing UI elements here
}));
person Vladimir    schedule 16.06.2015
comment
Если вы находитесь в модели представления, это не сработает. 'this', вероятно, не имеет диспетчера. Только элементы пользовательского интерфейса. Поэтому, если я попытаюсь выполнить работу с пользовательским интерфейсом, например, обновить связанную ObservableCollection из фонового потока, я не смогу этого сделать. - person Anthony Russell; 20.07.2016
comment
@AnthonyRussell да, это правда. Он не говорит, использует ли он объект из кода, за которым, как я полагаю, он стоит. В этом случае это сработает. - person Vladimir; 20.07.2016

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

После получения ошибки компиляции SetValue/GetValue, описанной здесь, мне нужно было получить свою виртуальную машину из DependencyObject.

Я хотел продолжить наследование моей виртуальной машины от нашей ViewModelBase, которая была получена от нашего класса AbstractNotifyPropertyChanged (конечно, не может быть получена от двух полных классов в C#). Будучи очень умным парнем, я решил добавить производную DependencyObject в свой класс AbstractNotifyPropertyChanged. Я не видел причин, по которым это небольшое изменение могло бы иметь какие-либо пагубные последствия. В течение многих недель приложение работало нормально.

Всегда есть большое волосатое «однако». Во время тестирования было обнаружено, что одно из моих полей со списком приводило к сбою всего приложения при выборе элемента. Это произошло из-за необработанного исключения InvalidOperationException. «Вызывающий поток не может получить доступ к этому объекту, потому что им владеет другой поток». Единственное, что отличало это поле со списком, это то, что оно использовало DependencyProperty для некоторых значений.

Четыре дня утомительной отладки ни к чему не привели, потому что ни один из моих кодов не вызывал вызовов. Все это исходило от WPF и взрывалось, когда стек вызовов достигал VerifyAccess в GetValue. Очевидно, что когда он вызывается фреймворком, попытка вызвать Invoke для пересечения потоков не произойдет? Куда бы вы поместили вызов Invoke?

Так как обычная отладка не удалась, мне пришлось пойти по сложному пути. Я повторил свои шаги регистрации, чтобы узнать, когда в последний раз код работал. У меня уже была идея, что это каким-то образом связано с DependencyProperty выпадающего списка, поэтому, увидев изменения кода, мои подозрения указывали на происхождение от DependencyObject.

После некоторой обработки производных кода для удаления DependencyObject из цепочки для всех моих ViewModels (через ViewModelBase) и размещения этого производного только в тех местах, которые мне нужны, проблема была решена.

person Stubby    schedule 10.09.2018