I'm developing a Universal Windows Platform app using XAML that runs on a Raspberry Pi under Windows 10 IoT Core
. The app drives a temperature sensor that's on the I2C bus. The sensor class is MLX90614Thermometer
. The sensor uses a DispatcherTimer
to take readings every 100 milliseconds (approx) and updates a moving average. When the value of the moving average changes by more than a specified threshold, the sensor raises a ValueChanged
event and provides the new value in the event args.
In my ViewModel class, TemperatureSensorViewModel
, I subscribe to the sensor's ValueChanged
event and use it to update bound properties named Ambient
, Channel1
and Channel2
. These properties are bound to text blocks in the XAML UI. Here is the event handler:
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;
}
}
...and here is a sample data binding for Ambient
...
<TextBlock x:Name="Ambient" Grid.Row="1" Text="{Binding Path=Ambient}" Style="{StaticResource FieldValueStyle}" />
I'm using the MVVM Light Toolkit, so my properties are implemented like this (only Ambient
shown, but the others are identical except in name):
public double Ambient
{
get { return ambientTemperature; }
private set { Set(nameof(Ambient), ref ambientTemperature, value); }
}
The MVVM Light Toolkit provides the Set()
method, which automatically raises the PropertyChanged
notification for the property being set.
This works correctly if I read a single sample from the sensor in response to a button press. As soon as I enable the automatic sampling mode (which is timer based) though, it starts throwing COMExceptions
. So this must be some kind of threading issue related to the timer.
Now, if I understand correctly, the runtime is supposed to marshal PropertyChanged
notifications onto the UI thread automatically; and that does seem to be the case from looking at the stack trace. However, I eventually get a COMException
. Ugh.
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? I don't understand what's happening here. Can anyone see what the problem might be?