3

これは私の最初の投稿です。実際、私は通常、ここで見つけることができる素晴らしい投稿データベースですべての問題を解決します。しかし、私は実際に今立ち往生しています:

COMオブジェクトを含むMVVMに続くプロジェクトに取り組んでいます。調査中に読んだように、COM オブジェクトにはそれを作成したスレッドからのみアクセスできることがわかりました。私のCOMオブジェクトは次のインターフェースを実装しています

interface IComUpdate
{
    void Update();
}

そのため、COM オブジェクトを作成すると、更新があるたびに (いつ、ランダムかはわかりません)、COM サーバーは、Update()実装した COM オブジェクト クラスの を呼び出します。

私の目標は、COM オブジェクトが UI スレッドとは独立して存在する COM オブジェクト スレッドに名前を付けて、別のスレッドを作成することでした。そのため、更新があるたびに、UI スレッドとは別のスレッドで処理します。

実際にそれは働いています:

ViewModel の冒頭で、特定のオブジェクトのコレクションを作成します。

このオブジェクトModelObjは、モデルの一部であり、いくつかの変数の初期化とは別に、アプリケーションが COM オブジェクトの新しいスレッドを作成して開始する静的コンストラクターを定義します。

Thread t = new System.Threading.Thread(() =>
           {
               System.Threading.Thread.CurrentThread.Name = "Thread of COM Object";
               IComUpdate myComObj;
               myComObj = (IComUpdate)Activator.CreateInstance(blabla);
               Application.Run();
           });

t.SetApartmentState(ApartmentState.STA);
t.Start();

それは実際には非常にうまく機能しUpdate()ます.COMオブジェクトの実装では、スレッドが作成されたばかりでUIスレッドではないことが実際にわかります。

今問題はこれです: これModelObjは私が作成するINotifyPropertyChangedインターフェイスを実装します。

私の考えは次のとおりです。COM オブジェクトが更新を受け取るたびに、COM オブジェクト スレッドからデータを処理し、このスレッドからインスタンスのいくつかのプロパティを更新します。したがって、これらのプロパティは、私のスレッドと UI スレッドModelObjのプロパティの変更を発生させます。ModelObjユーザー インターフェイスを更新します。

UI の更新に時間がかかりすぎるとUpdate()、画面に表示されないものもあるかもしれませんが、COM オブジェクトはそれらをModelObjインスタンスに記録するので、UI がすべての更新をキャッチすることはあまり重要ではありません。COM オブジェクトは必要ありませんでした。 UI が更新されて再度呼び出されるまで待機する必要があります。

RaisePropertyChanged("property")たくさんの投稿を読んで、失敗するだろうと思いました。

実際には、COM オブジェクトのスレッドでもRaisePropertyChanged正常に実行されるため、コードをトレースすると、ViewModel アセンブリに切り替わることがわかります。

// Here I'm still in the thread of my COM object!
base.NotifyOfPropertyChange<string>(() => this.property)

そしてUIアップデート。

注:私は、WPF のビューと ViewModel の間のバインドに Caliburn Micro を使用しています。

なので、これ以降は追跡できませんbase.NotifyOfPropertyChange<string>(() => this.property)。Caliburn がスレッドの切り替えを処理しているのかもしれませんが、これは実際には私の問題ではありません。

私が言えることは、私の COM オブジェクト スレッドは、UI が更新されて私RaisePropertyChanged("property")の .

UI が実際に更新されるかどうかを知らずに、COM オブジェクト スレッドを更新して、ModelObj更新するメッセージを UI に送信し (これの一部のフィールドが変更されたため)、すぐModelObjに続行する必要があります。

誰かがこの振る舞いについて考えを持っていますか?

どうもありがとうございました。

####アップデート####

このような迅速な回答に感謝します。

Zdeslav Vojkovicが示唆したように、私は実際にやった:

常に GUI スレッドから GUI を更新する必要があります

完全を期すために、私が行った方法は次のとおりです。

私のビューはコード ビハインドのない完全な WPF であるため、BeginInvoke を呼び出すためのコントロールやフォームがないため、ModelObj の静的コンストラクターで、BeginInvoke を呼び出すことができるようにするためだけに、UI スレッドから非表示のコントロールを作成しました。 .

だから私はそれを宣言しました:

public static Control mInvokeControl;
delegate void MyDelegate();
private MyDelegate _NotifyDelegate;

そして、私のオブジェクトの静的コンストラクターでこれを行いました:

mInvokeControl = new Control();
mInvokeControl.CreateControl();

通常のコンストラクターでは、次のようにデリゲートを初期化します。

_NotifyDelegate = new MyDelegate(this.NotifyByInvoke);

次に、このように使用した後:

ModelObj.mInvokeControl.BeginInvoke(this._NotifyDelegate );

メソッドは次のとおりです。

public void NotifyByInvoke()
{
    RaisePropertyChanged("Update");
}

すべて正常に動作します!

4

2 に答える 2

3

COMObj は、それを作成したスレッドからのみアクセスできます

本当じゃない。オブジェクト アパートメント モデルによって異なりますが、通常は任意のスレッドからアクセスでき、同じスレッドで呼び出されるか、適切なスレッドにマーシャリングされます。

あなたの問題は、主要なノーノーであるバックグラウンドスレッドからGUIを更新することだと思います。常に GUI スレッドから GUI を更新する必要があります。モデル オブジェクトを更新すると、バックグラウンド スレッドで引き続き発生しINotifyPropertyChanged、そのスレッドでインターフェイスのイベントが発生します。

次のようなものを使用して、モデルの更新を GUI スレッドに同期する必要があります (WPF ではなく WinForms - WPF を使用する必要がありますfrm.Dispatcher.BeginInvokeが、問題は同じです)。

プライベート デリゲート void ExecuteActionHandler(アクション アクション);

public static void ExecuteOnUiThread(this Form form, Action action)
{
  if (form.InvokeRequired) { // we are not on UI thread
    // Invoke or BeginInvoke, depending on what you need
    // but you said ' and continue immediatly' so BeginInvoke it is 
    form.BeginInvoke(new ExecuteActionHandler(ExecuteOnUiThread), action);
  }
  else { // we are on UI thread so just execute the action
    action();
  }
}

同様の問題に関する別の質問があり、そこに追加の詳細を提供しました。

于 2013-03-13T10:29:36.400 に答える
0

処理するデータの量や、GUI 部分の実行にかかる時間はわかりません。ロックされたキューの使用を検討することもできます。ModelObj 内でキューを使用して、新しいタスクをキューに入れることができます。これは、あなたが得るすべてのもので行います。次に、(GUI スレッド上に) タイマー スレッドがある場合があります。

ここでは、GUI に表示する新しいデータがあるかどうか、ロックされたキューを確認するだけです。ここで完全なリストをローカルでデキューできます。次に、1 つのコンポーネントに表示するデータが複数あるかどうかを確認することもできます。このようにして、新しい更新が既にある更新をスキップできます。また、GUI スレッドを呼び出してアクションを実行する時間をスキップします。一度に複数の GUI 更新を行うことができます。やるべきことが多すぎる場合は、GUI がユーザーの操作に反応できるように、特定の数のアイテムだけをキューから取り出すことができます。ただし、キューが常に増加していないことを確認する必要があります。

于 2013-03-13T11:11:23.093 に答える