これを行う方法はたくさんあります。カスタム イベント、デリゲート、および Invoke() を使用する従来のアプローチを紹介します。そのプロセスを理解することが重要だと思います。これを理解したら、いくつかの新しいアプローチにジャンプできます。
まず、SerialThing() クラスで、受信時にデータを渡すカスタム イベントを宣言します。
class SerialThing
{
public delegate void DataReceivedDelegate(string data);
public event DataReceivedDelegate DataReceived;
static SerialPort myDevice;
public SerialThing()
{
myDevice = new SerialPort();
myDevice.DataReceived += new SerialDataReceivedEventHandler(myDevice_DataReceived);
}
void myDevice_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
// ... grab the data and place into a string called "data" ...
string data = "";
// raise our custom event:
if (DataReceived != null)
{
DataReceived(data);
}
}
}
次に、Form1 で、SerialThing のインスタンスを作成するときにそのカスタム イベントをサブスクライブします。さらに、そのイベントが受信されると、InvokeRequired、Invoke、およびデリゲートを使用して、セカンダリ スレッドからメイン スレッドへの呼び出しをマーシャリングします。
public partial class Form1 : Form
{
SerialThing mySerialThing;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
mySerialThing = new SerialThing();
mySerialThing.DataReceived += new SerialThing.DataReceivedDelegate(mySerialThing_DataReceived);
}
private delegate void DataReceivedDelegate(string data);
void mySerialThing_DataReceived(string data)
{
if (this.InvokeRequired)
{
this.Invoke(new DataReceivedDelegate(mySerialThing_DataReceived), new Object[] { data });
}
else
{
textBox1.Text = data;
}
}
}
編集:以下のコメントに応えて...
デリゲートは、単に「メソッドへのポインター」と考えてください。デリゲートを実行すると、関連するメソッドが実行されます。
InvokeRequired() 部分は、コントロールを作成したスレッドとは異なるスレッドでコードが実行されているかどうかを判断します。この場合、コントロールはフォーム自体です ( this
)。true が返された場合、イベントは別のスレッドで受信されました。次に、ブロックthis.Invoke()
の真の部分の内側に線を引きます。If
再びthis
フォームを参照します。したがって、フォームは、それを作成したスレッド (メイン UI スレッド) で渡されたデリゲートの呼び出し (「実行」) を要求しています。再帰呼び出しの結果、既に存在しているのと同じメソッドを実際に指すデリゲートのインスタンスを作成します。2 番目のパラメーターは、デリゲートと共にパラメーターを渡すために使用される Object の配列です。
Invoke() が実行されると、再帰呼び出しのためにメソッドに再度入ることになります。ただし、この時点では、メイン UI スレッドで実行しているため、InvokeRequired() チェックは false を返します。If
したがって、TextBox を更新するステートメントの false 部分にドロップダウンします。このパターンでは、ステートメントのelse
ブロックでGUI コントロールを更新しても安全です。If
ここでは再帰呼び出しは必要ないことに注意してください。これは単にスタイルの選択です。代わりに、デリゲートが指す 2 番目の「ヘルパー」関数を使用して、代わりにそれを呼び出すこともできました。再帰的なアプローチにより、必要なメソッドの数が減ります。
これはおそらく、この種の問題を解決するための最も詳細なアプローチです。ただし、イベントとデータの流れ、およびスレッド間の移動を示しているので、気に入っています。
匿名デリゲートを使用して、すべてのフォーム コードをこれだけに短縮できます。
private void Form1_Load(object sender, EventArgs e)
{
mySerialThing = new SerialThing();
mySerialThing.DataReceived += delegate (string data)
{
this.Invoke((MethodInvoker)(delegate() { textBox1.Text = data; }));
};
}
私はあなたのことを知りませんが、元 VB6 プログラマーとして、最初にそのようなものを見たときは奇妙に見えます。
また、さまざまなスレッドで実行されていることがわかっているコンポーネントを使用しましたが、「フォームコード」はデリゲートのものを使用する必要がなかったため、クラスに埋め込むことができるものがあるのでしょうか?
はい、何らかの「魔法」をクラスに焼き付けて、既にメイン UI スレッドでイベントを発生させ、Invoke() 呼び出しを必要としないようにすることは可能です。これを行う 1 つの方法は、SynchronizationContextを使用することです。
このタイプの問題にアプローチする別の可能性は、メイン UI スレッドで発生する ProgressChanged() や RunWorkerCompleted() などのイベントを持つ BackgroundWorker() コントロールを使用することです (これらは内部で必要な呼び出しタイプのものを実行します)。あなたのために)。