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?