Я работаю над приложением WinRT/Windows Store, написанным на C#, который использует MVVM Light. Я пытаюсь изменить метод OnPropertyChanged() MVVM Light, чтобы уведомления о событиях всегда возникали в потоке пользовательского интерфейса, в противном случае элементы интерфейса, привязанные к моим свойствам модели просмотра, будут пропускать уведомления о событиях, поскольку они должны возникать в потоке пользовательского интерфейса. В настоящее время я пробую этот код:Получение интерфейса для маршалинга для другого потока Исключение с вызовом диспетчера потоков пользовательского интерфейса?
/// <summary>
/// Event handler for the PropertyChanged event.
/// </summary>
/// <param name="propertyName">The name of the property that has changed.</param>
protected void OnPropertyChanged(string propertyName = null)
{
var eventHandler = PropertyChanged;
if (eventHandler != null)
{
// Make sure the event notification occurs on the UI thread or the INotifyPropertyChanged
// notification will not be seen by the consumer of this event or worse,
// a "wrong thread" COM Exception will be raised if we are not on the UI thread.
var dispatcher = Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher;
dispatcher.RunAsync(dispatcher.CurrentPriority,
() =>
{
eventHandler(this, new PropertyChangedEventArgs(propertyName));
});
}
}
Я использовал код, подобный этому, прежде чем нажимать код на поток пользовательского интерфейса. Однако в этом случае, я получаю следующее исключение:
Первый шанс исключение типа «System.Exception» произошло в Common_WinStore.DLL
WinRT информация: приложение под названием интерфейс, который был ранжированы для другого потока.
Это сбивает с толку, так как точка CoreWindow.Dispatcher является запланировать выполнение кода из потока без пользовательского интерфейса на потоке пользовательского интерфейса. Я прочитал много сообщений об ошибках маршалинга на SO, но ни один из них не имеет дело с получением исключений маршалинга при попытке вызвать RunAsync() на диспетчере сам. Вместо этого они используют код, который я использую, чтобы вылечить возникновение этого Исключения.
Почему я получаю это исключение с Dispatcher.RunAsync() и как его исправить?
КОНТЕКСТ ПРИМЕЧАНИЕ. Я попал в эту ситуацию из-за ситуации взаимоблокировки с некоторым кодом, инициированным элементами интерфейса, которые привязаны к свойствам WriteableBitmap в моей модели представления. ждут звонки зашли бы в тупик, когда ожидаемая задача async попыталась продолжить исходный поток, который был потоком пользовательского интерфейса, и этот поток ожидал завершения вызова. Чтобы решить эту проблему, я добавил ConfigureAwait (false) в wait оператор, чтобы освободить вызов из контекста потока пользовательского интерфейса. Это привело к ошибке Исключение COM, когда MVNM Light OnNotifyPropertyChanged() попытался обновить свойство. Вот почему я пытаюсь вернуть этот код обратно в поток пользовательского интерфейса. Я хочу сделать это в любом случае, потому что, если мне это удастся, у меня нет беспокойства о том, что уведомления о событиях происходят должным образом, независимо от контекста вызова.
ОБНОВЛЕНИЕ: Я добавил AsyncEx библиотеку, предложенную Стивеном Клири. Пока это работает. Сначала у меня была ловушка, как вы можете видеть в ответе на свой ответ. Объекты, которые у меня есть, содержат поле байтов JPEG, которое десериализуется в SQLite-движке. Это означало, что мой конструктор без параметров и поэтому инициирует метод async для преобразования байтов JPEG в объект с возможностью связывания WriteableBitmap. Это произошло потому, что метод асинхронного преобразования начал работать до того, как необходимо было десериализовать байты для преобразования.
Я решил это с помощью объекта AsyncManualResetEvent, который вначале находится в неустановленном состоянии. Свойство set для свойства JPEG bytes задает событие. Метод async-преобразования ожидает этого объекта события и поэтому освобождается для выполнения преобразования сразу после того, как доступны байты.Я отправляю отрывки кода ниже, чтобы увидеть, если Стивен и другие из вас увидеть любые потенциальные проблемы с реализацией или если есть более простой способ для достижения этой цели:
// Create an Async manual reset event for the JPEG conversion method in the unset state.
private AsyncManualResetEvent _asyncManResetEvent = new AsyncManualResetEvent(false);
/// <summary>
/// The jpeg bytes for the JPEG image for the videomark.
/// </summary>
private byte[] _thumbnailJpegBytes;
public byte[] ThumbnailJpegBytes
{
get
{
return this._thumbnailJpegBytes;
}
set
{
SetProperty(ref this._thumbnailJpegBytes, value, "ThumbnailJpegBytes");
// Release the lock so that the async call waiting to convert the JPEG bytes to
// a WriteableBitmap can finish up.
_asyncManResetEvent.Set();
}
}
// The WriteableBitmap property.
[Ignore]
public INotifyTaskCompletion<WriteableBitmap> ThumbnailAsync
{
get;
private set;
}
// The async method passed to the NotifyTaskCompletion constructor.
async private Task<WriteableBitmap> ConvertJpegBytesToThumbnail()
{
// Wait for data to be available.
await this._asyncManResetEvent.WaitAsync();
return await Misc.JpegBytesToBitmap(ThumbnailJpegBytes);
}
Это произошло неправильно, когда вы создали непереходный объект в неправильном потоке. Windows дала ему безопасный дом, * другой * поток, который может гарантировать, что он используется поточно-безопасным способом. Этот объект подписал событие PropertyChanged. Он получил обратный вызов в потоке пользовательского интерфейса, но это не тот поток, на котором он действительно доволен. Он хочет получить обратный вызов в своей собственной ветке. Поиск этого объекта и его создание в основном потоке пользовательского интерфейса - это решение, над которым вам нужно работать. Решает эту проблему взаимоблокировки. –
HansPassant Потребитель - это элемент пользовательского интерфейса, доступ к которому осуществляется через привязку данных. Я не могу изменить поток, на котором работает объект. –