2

Raspberry Pi の下で実行される XAML を使用して、ユニバーサル Windows プラットフォーム アプリを開発していWindows 10 IoT Coreます。アプリは、I2C バス上の温度センサーを駆動します。センサークラスはMLX90614Thermometer. センサーは aDispatcherTimerを使用して 100 ミリ秒 (約) ごとに読み取り値を取得し、移動平均を更新します。移動平均の値が指定されたしきい値を超えて変化すると、センサーはイベントを発生させValueChanged、イベント引数に新しい値を提供します。

私の ViewModel クラス ではTemperatureSensorViewModel、センサーのイベントをサブスクライブし、それを使用して、およびValueChangedという名前のバインドされたプロパティを更新します。これらのプロパティは、XAML UI のテキスト ブロックにバインドされています。イベント ハンドラは次のとおりです。AmbientChannel1Channel2

    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 Toolkitを使用しているため、プロパティは次のように実装されています (Ambient表示されているだけですが、名前以外は同じです)。

    public double Ambient
    {
        get { return ambientTemperature; }
        private set { Set(nameof(Ambient), ref ambientTemperature, value); }
    }

MVVM Light Toolkit は、設定されているプロパティの通知Set()を自動的に発生させるメソッドを提供します。PropertyChanged

ボタンの押下に応答してセンサーから単一のサンプルを読み取った場合、これは正しく機能します。ただし、自動サンプリング モード (タイマー ベース) を有効にするとすぐに、スローが開始されますCOMExceptions。したがって、これはタイマーに関連するある種のスレッドの問題である必要があります。

ここで、私の理解が正しければ、ランタイムはPropertyChanged通知を UI スレッドに自動的にマーシャリングすることになっています。スタック トレースを見ると、そのように見えます。しかし、最終的にはCOMException. うーん。

System.Runtime.InteropServices.COMException (0x8001010E): アプリケーションは、別のスレッド用にマーシャリングされたインターフェイスを呼び出しました。(HRESULT からの例外: 0x8001010E (RPC_E_WRONG_THREAD))
   System.Runtime.InteropServices.WindowsRuntime.PropertyChangedEventArgsMarshaler.ConvertToNative (PropertyChangedEventArgs managedArgs) で
   System.ComponentModel.PropertyChangedEventHandler.Invoke (オブジェクトの送信者、PropertyChangedEventArgs e) で
   GalaSoft.MvvmLight.ObservableObject.RaisePropertyChanged (文字列プロパティ名) で
   GalaSoft.MvvmLight.ViewModelBase.RaisePropertyChanged[T] (文字列 propertyName、T oldValue、T newValue、ブール値ブロードキャスト) で
   GalaSoft.MvvmLight.ViewModelBase.Set[T] (文字列 propertyName、T& フィールド、T newValue、ブール値ブロードキャスト) で
   TA.UWP.Devices.Samples.ViewModel.TemperatureSensorViewModel.set_Channel1 で (2 倍の値)
   TA.UWP.Devices.Samples.ViewModel.TemperatureSensorViewModel.HandleSensorValueChanged (オブジェクト送信者、SensorValueChangedEventArgs e) で
   at TA.UWP.Devices.MLX90614Thermometer.RaiseValueChanged(UInt32 チャネル、Double 値)
   TA.UWP.Devices.MLX90614Thermometer.SampleAllChannels() で
   TA.UWP.Devices.MLX90614Thermometer.b__37_0() で
   System.Threading.Tasks.Task.InnerInvoke() で
   System.Threading.Tasks.Task.Execute() で
--- 例外がスローされた前の場所からのスタック トレースの終わり ---
   System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (タスク タスク) で
   System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (タスク タスク) で
   System.Runtime.CompilerServices.TaskAwaiter.GetResult() で
   TA.UWP.Devices.MLX90614Thermometer.d__37.MoveNext() で
--- 例外がスローされた前の場所からのスタック トレースの終わり ---
   System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (タスク タスク) で
   System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (タスク タスク) で
   System.Runtime.CompilerServices.TaskAwaiter.GetResult() で
   TA.UWP.Devices.MLX90614Thermometer.d__38.MoveNext() で

何?ここで何が起こっているのかわかりません。誰が問題が何であるかを見ることができますか?

4

2 に答える 2

5

さらに調査した後、私は自分の質問に答えることができると思います...

UI スレッドに自動的にマーシャリングされる PropertyChanged イベントについて、私は無効な仮定をしたようです。WPF に関する記事のいくつかの場所でそれを読みましたが、@Clemens がコメントで指摘したように、これは私たちが話している WPF ではなく、Windows ランタイム (WinRT) の派生物であるユニバーサル Windows プラットフォームです。

  • 重要な学習: UWP の XAML ≠ WPFUniversal Windows Apps を検討する場合、WPF のドキュメントに頼ることはできません

次に、私と似たこの質問を見つけました。具体的には、ポスターが WPF を扱っていると仮定して間違いを犯したことです。受け入れられた回答により、MVVM Light Toolkit のDispatcherHelperclassに関するこの他の質問にたどり着きました。これは、任意のコードをディスパッチャー スレッドにマーシャリングするために使用できます。

そのため、独自のスレッド マーシャリングを行う必要があるようです (私は、Windows プログラミングのこの側面が本当に嫌いです。Microsoft がスレッドセーフな UI テクノロジを作成してくれることを願っています!)。

したがって、このパターンを使用するようにプロパティを更新しました。

    public double Ambient
    {
        get { return ambientTemperature; }
        private set
        {
            ambientTemperature = value;
            DispatcherHelper.CheckBeginInvokeOnUI(() => RaisePropertyChanged());
        }
    }

これで期待どおりに動作するようになりました。

多くの人がこの泥沼に陥ると思うので、必要なときに人々が見つけてくれることを願って、この回答をここに残しておきます.

于 2015-12-24T02:11:39.373 に答える