Я разрабатываю приложение универсальной платформы Windows с использованием XAML, которое работает на Raspberry Pi под Windows 10 IoT Core
. Приложение управляет датчиком температуры, подключенным к шине I2C. Класс датчика MLX90614Thermometer
. Датчик использует DispatcherTimer
для снятия показаний каждые 100 миллисекунд (приблизительно) и обновляет скользящее среднее значение. Когда значение скользящего среднего изменяется более чем на заданный порог, датчик вызывает событие ValueChanged
и предоставляет новое значение в аргументах события.
В моем классе ViewModel TemperatureSensorViewModel
я подписываюсь на событие датчика ValueChanged
и использую его для обновления связанных свойств с именами Ambient
, Channel1
и Channel2
. Эти свойства привязаны к текстовым блокам в пользовательском интерфейсе XAML. Вот обработчик события:
void HandleSensorValueChanged(object sender, SensorValueChangedEventArgs e)
{
switch (e.Channel)
{
case 0:
Ambient = e.Value;
break;
case 1:
Channel1 = e.Value;
break;
case 2:
Channel2 = e.Value;
break;
}
}
...и вот пример привязки данных для Ambient
...
<TextBlock x:Name="Ambient" Grid.Row="1" Text="{Binding Path=Ambient}" Style="{StaticResource FieldValueStyle}" />
Я использую набор инструментов MVVM Light, поэтому мои свойства реализованы следующим образом (показаны только Ambient
, остальные идентичны, кроме названия):
public double Ambient
{
get { return ambientTemperature; }
private set { Set(nameof(Ambient), ref ambientTemperature, value); }
}
MVVM Light Toolkit предоставляет метод Set()
, который автоматически создает уведомление PropertyChanged
для устанавливаемого свойства.
Это работает правильно, если я считываю один образец с датчика в ответ на нажатие кнопки. Однако, как только я включаю режим автоматической выборки (основанный на таймере), он начинает выдавать COMExceptions
. Так что это должно быть какая-то проблема с потоками, связанная с таймером.
Теперь, если я правильно понимаю, среда выполнения должна автоматически маршалировать уведомления PropertyChanged
в поток пользовательского интерфейса; и это действительно так, если посмотреть на трассировку стека. Однако в итоге я получаю COMException
. Фу.
System.Runtime.InteropServices.COMException (0x8001010E): The application called an interface that was marshalled for a different thread. (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD)) at System.Runtime.InteropServices.WindowsRuntime.PropertyChangedEventArgsMarshaler.ConvertToNative(PropertyChangedEventArgs managedArgs) at System.ComponentModel.PropertyChangedEventHandler.Invoke(Object sender, PropertyChangedEventArgs e) at GalaSoft.MvvmLight.ObservableObject.RaisePropertyChanged(String propertyName) at GalaSoft.MvvmLight.ViewModelBase.RaisePropertyChanged[T](String propertyName, T oldValue, T newValue, Boolean broadcast) at GalaSoft.MvvmLight.ViewModelBase.Set[T](String propertyName, T& field, T newValue, Boolean broadcast) at TA.UWP.Devices.Samples.ViewModel.TemperatureSensorViewModel.set_Channel1(Double value) at TA.UWP.Devices.Samples.ViewModel.TemperatureSensorViewModel.HandleSensorValueChanged(Object sender, SensorValueChangedEventArgs e) at TA.UWP.Devices.MLX90614Thermometer.RaiseValueChanged(UInt32 channel, Double value) at TA.UWP.Devices.MLX90614Thermometer.SampleAllChannels() at TA.UWP.Devices.MLX90614Thermometer.b__37_0() at System.Threading.Tasks.Task.InnerInvoke() at System.Threading.Tasks.Task.Execute() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter.GetResult() at TA.UWP.Devices.MLX90614Thermometer.d__37.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter.GetResult() at TA.UWP.Devices.MLX90614Thermometer.d__38.MoveNext()
WAT? Я не понимаю, что здесь происходит. Кто-нибудь может увидеть, в чем проблема?