トップ回答者
delegate基礎 別スレッドからComboboxのテキストを取得するには

質問
-
前提
- VisualBasic2013
- Windows7 pro SP1 x64
- Windows Formアプリケーション
- .NET Framework 4
シリアルポートの通信アプリケーションで、データ受信時に起動するスレッドからフォーム上に配置されたCombobox2の選択テキストを読み出すことができず困つてゐます。
今回のソフトウェアは以前作成した通信ソフトウェアを元に改造するものです。
この時は受信データをTextboxに表示するため、返し値のないプロシージャーをデリゲートから呼び出す処理をしてをり動作してゐました。
今回はCombobox2.Textの読み出しと言ふことでPublic Delegate Function textDelegate() As String
と関数型のデリゲートを定義し、呼び出し箇所で
Dim dlgt As textDelegate = New textDelegate(AddressOf Combobox2.Text)
Dim text As String = dlgt()としましたがCombobox2.Textの部分に下線がつきエラーになりビルドできません。
メソッド 'Public Overrides Property Text As String' に、
デリゲート 'Delegate Function textDelegate() As String'
と互換性があるシグネチャがありませんデリゲート呼び出し時にComboBox2.Textを直に指定するのが悪いのかと、下記combotext()の関数を定義して間接的記法を実験しました。しかしビルドできるものの動作時に別スレッドからの呼び出しエラーとなり解決に至りません。
Private Function combotext() As String
Return ComboBox2.Text
End Function
何を考へ違ひしてゐるのかヒントをいただきたく回答をお待ちしてをります。
以下にコードの構造を示します。
Public Class Form1
省略
'//デリゲートの宣言
Public Delegate Sub MyDelegate(ByVal msg() As Byte)
Public Delegate Function textDelegate() As String
省略
'serialPort1のDataReceivedイベント
Private Sub SerialPort1_DataReceived(ByVal sender As System.Object, _
ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
省略
packet_parse()
省略
End Sub
Private Sub packet_parse()
省略
Dim dlgt As textDelegate = New textDelegate(AddressOf Combobox2.Text)
Dim text As String = dlgt()
省略
End Sub
省略
End Class
回答
-
基本的に別スレッドからコントロールに対して取得・設定の操作をすることはできないように設計されています。
この原則に対して、SerialPort の DataReceived イベントは別スレッドから実行されるため、コントロールの Invoke メソッドを経由することになります。参考例
https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q12104816777
https://kana-soft.com/tech/sample_0008_4.htmただ、Invoke を使って呼び出し元と連携するコード(戻り値をもらって処理する)というのは、ラムダ式が絡むなど、難しいかと思いますので、イベントが来た途端、すべての処理を Invoke でメインスレッドにお願いする形を考えてみてはいかがでしょうか。
(Packet_parse 自体を Invoke で呼ぶということ)- 編集済み AzuleanMVP, Moderator 2019年2月24日 8:56
- 回答としてマーク kusanagi42 2019年2月26日 10:13
-
プロパティでは、そのままではメソッドにはなりませんので、デリゲートとすることはできません。
ラムダ式を書くか、「Text プロパティから取得した String を返す」メソッドを自分で別途書くかのいずれかが必要です。// あえて旧仮名使いをするのは読みづらくするだけなのでやめた方が良いと思いますが・・・。
- 回答としてマーク kusanagi42 2019年2月25日 14:18
すべての返信
-
基本的に別スレッドからコントロールに対して取得・設定の操作をすることはできないように設計されています。
この原則に対して、SerialPort の DataReceived イベントは別スレッドから実行されるため、コントロールの Invoke メソッドを経由することになります。参考例
https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q12104816777
https://kana-soft.com/tech/sample_0008_4.htmただ、Invoke を使って呼び出し元と連携するコード(戻り値をもらって処理する)というのは、ラムダ式が絡むなど、難しいかと思いますので、イベントが来た途端、すべての処理を Invoke でメインスレッドにお願いする形を考えてみてはいかがでしょうか。
(Packet_parse 自体を Invoke で呼ぶということ)- 編集済み AzuleanMVP, Moderator 2019年2月24日 8:56
- 回答としてマーク kusanagi42 2019年2月26日 10:13
-
.NET Frameworkのプログラミングにおいて、全てに型があります。変数を定義する場合、必ず型が必要です。
では、関数を入れるための変数であるデリゲートの型は何でしょうか? 関数の定義は無限にあります。よって、事前に型を用意することができません。よって、デリゲートという型をプログラマが定義して用意します。
これで関数の型の定義ができ、関数を入れるための変数を用意できました。この変数を経由してスレッド間が跨げます。
この変数に入れた関数をスレッドから呼び出すために、Invokeを使用します。当然ながら変数(デリゲート)に関数をセットしたスレッドではInvokeは必要ありません。Invokeが必要かどうかを判定するメソッドであるInvokeRequiredも用意されています。
スレッド間を跨いで実行することをマーシャリングといいます。マーシャリングを行うために、Invoke、BeginInvoke、EndInvokeが用意されています。
★良い回答には質問者は回答済みマークを、閲覧者は投票を!
- 編集済み trapemiyaModerator 2019年2月25日 0:34 誤字修正
-
プロパティでは、そのままではメソッドにはなりませんので、デリゲートとすることはできません。
ラムダ式を書くか、「Text プロパティから取得した String を返す」メソッドを自分で別途書くかのいずれかが必要です。// あえて旧仮名使いをするのは読みづらくするだけなのでやめた方が良いと思いますが・・・。
- 回答としてマーク kusanagi42 2019年2月25日 14:18
-
コントロールを別スレッドから操作すると InvalidOperationException エラーが発生しますが、.NET Framework 1.x には、この制限はありませんでした。
2.0 からエラーが発生するようになったのですが、互換性を保つためのプロパティ Control.CheckForIllegalCrossThreadCalls を False にすると、エラーが発生しなくなります。
GDI 関連を別スレッドから操作すると問題が発生することがありますが、Text プロパティの読み出しや書き込みは、メッセージキューを経由してシリアライズされるため問題ないと思います。
Control.CheckForIllegalCrossThreadCalls = False Dim text As String = ComboBox2.Text Control.CheckForIllegalCrossThreadCalls = True
真っ当な方法ならばこんな感じでしょうか。
Dim invoker As Func(Of String) = Function() Return ComboBox1.Text End Function Dim text As String = DirectCast(Me.Invoke(invoker), String)