質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
VB.NET

Microsoft Visual Basic .NETのことで、Microsoft Visual Basic(VB6)の後継。 .NET環境向けのプログラムを開発することができます。 現在のVB.NETでは、.NET Frameworkを利用して開発を行うことが可能です。

Q&A

解決済

1回答

4496閲覧

別スレッドからのコントロール

退会済みユーザー

退会済みユーザー

総合スコア0

VB.NET

Microsoft Visual Basic .NETのことで、Microsoft Visual Basic(VB6)の後継。 .NET環境向けのプログラムを開発することができます。 現在のVB.NETでは、.NET Frameworkを利用して開発を行うことが可能です。

0グッド

0クリップ

投稿2016/08/13 21:17

編集2016/08/14 13:12

本スレッドでの処理をbackgroundworkerで別スレッドで実行します。
別スレッドの処理結果を本スレッドのテキストボックスに反映させたいのですがうまくいきません。

まず、backgroundworker1_DoWorkでの処理の結果をBackgroundWorker1_ProgressChangedで受け取れていないみたいです。

どのようにすれば良いでしょうか。
よろしくお願いいたします。

Public Class bean Private no As Integer Private result As String Public Property No As Integer Get Return no End Get Set(value As Integer) no = value End Set End Property Public Property Result As String Get Return result End Get Set(value As String) result = value End Set End Property End Class Public Class clsMain Public Function prepare() As List(Of bean) Dim lst As New List(Of bean) Dim b As bean = Nothing b = New bean b.No = 1 lst.Add(b) ・ ・ Return lst End Function Public Sub check(lst As List(Of bean)) cal() ・ ・ End Sub End Class Public Class frmMain Private Sub btnStart_Click(sender As Object, e As EventArgs) Handles btnStart.Click BackgroundWorker1.WorkerSupportsCancellation = True BackgroundWorker1.WorkerReportsProgress = True BackgroundWorker1.RunWorkerAsync() End Sub Private Sub backgroundworker1_DoWork( ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) _ Handles BackgroundWorker1.DoWork Dim lst As List(Of bean) While True If BackgroundWorker1.CancellationPending Then Exit Sub End If Dim CLSMain As clsMain = New clsMain lst = CLSMain.prepare() CLSMain.check(lst) BackgroundWorker1.ReportProgress(0, lst) Threading.Thread.Sleep(60000) End While e.Result = lst End Sub Private Sub BackgroundWorker1_ProgressChanged( ByVal sender As Object, ByVal e As ProgressChangedEventArgs) _ Handles BackgroundWorker1.ProgressChanged Dim lst As List(Of bean) = DirectCast(e.UserState, List(Of bean)) End Sub Private Sub BackgroundWorker1_RunWorkerCompleted( ByVal sender As System.Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) _ Handles BackgroundWorker1.RunWorkerCompleted If e.Cancelled = True Then 'BackgroundWorker1.CancelAsync() Else Dim lst As List(Of bean) = DirectCast(e.Result, List(Of bean)) For Each r As bean In lst Dim rResult As Control() = Controls.Find("txtBox" & r.No, True) CType(rResult(0), TextBox).Text = r.Result Next End If End Sub Private Sub btnStop_Click(sender As Object, e As EventArgs) Handles btnStop.Click 'ストップボタンでBackgroundWorkerを停止させたい BackgroundWorker1.CancelAsync() End Sub End Class

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

「受け取れていない」の詳細がわかりませんが、lstに期待している値が入っていないという事でしょうか。
ざっと見ただけですが、気になる点をいくつか。

  • ReportProgresslstを渡していますが、BackgroundWorker1_ProgressChangedイベントで受け取っていません。

以下の様にProgressChangedEventArgsから取り出すようにしてください

Private Sub BackgroundWorker1_ProgressChanged( ByVal sender As Object, ByVal e As ProgressChangedEventArgs) _ Handles BackgroundWorker1.ProgressChanged Dim lst As List(Of bean) = DirectCast(e.UserState, List(Of bean))

そして、DoWorkのlstはFormのlstを使おうとしていますが、DoWorkで別途用意した方が良いでしょう(Dimを付けて別のlstにした方が良いでしょう)

  • BackgroundWorkerのDoWork内の処理結果は、DoWorkのDoWorkEventArgsのResultプロパティにセットしてください。

通常結果データは、DoWorkのReulstに値をセットして、RunWorkerCompletedイベントで受け取ります。
DoWorkイベントが終了するとRunWorkerCompletedイベントが発行されます。
ただ、今回は終了させないみたい?なので、このやり方は当てはまらないかもしれません。

** DoWorkイベントの最後 **

BackgroundWorker1.ReportProgress(0, lst) Threading.Thread.Sleep(60000) End While e.Result = lst End Sub

** RunWorkerCompleted **

Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted If (e.Cancelled) Then Else Dim lst As List(Of bean) = DirectCast(e.Result, List(Of bean)) ' 処理を書く End If End Sub
  • DoWorkでキャンセル判定をしているが、キャンセル可能でない可能性がある

これはこれからやるのかもしれませんが、BackgroundWorker1.WorkerSupportsCancellationにTrueをセットしていないので、キャンセル処理は受け付けられません(その場合エラーになる気がする)
※ デザイナでセットしているなら無視してください。


毎回1分待機させて終了させないBackgroundWorkerなら最初の指摘を直せば動きそうな気がします。
ただそういう場合、1分おきに実行はTimerに任せて、BackgroundWorkerは毎回終了するようにした方が良いように思います。


追記:8/14 15:14

提示のコードで試してみました。処理としては、e.Resultにlstをセットして、RunWorkerCompletedで受け取っているので、そこは問題ない様に見えます。
後述する「DoWorkで使っているlst変数が宣言されていない」(frmMainのものを使ってしまっていて状態が不明瞭に)という問題がありますが、最終的にはclsMainのprepareで何件のリストを返しているかによるので、そこの実際の処理を見直してみるしかなさそうです。

txtBox?の数字がbeanのNoときちんとあっているかは試してません。
Controls.Findとその次の行をコメントアウトして動くなら、受け渡しの問題ではありませんね。

vb

1For Each r As bean In lst 2 'Dim rResult As Control() = Controls.Find("txtBox" & r.No, True) 3 'CType(rResult(0), TextBox).Text = r.Result 4Next

長くてすいませんが、気になる点を指摘してみます。

まずは軽微なところから

  • Cancel処理で正常終了

ここは今のところの工夫ということで後回しで良いですが、CancelAsyncを読んだらキャンセルされて欲しいので、中断/終了は別の方法をとった方が良いでしょう。これは後で書きます。

  • beanのプロパティと内部変数名が同じ名前

VB.NETは大文字と小文字を区別しないので、privateのnoとPropertyのNoが二重定義でエラーになります。
これは質問用に書き換えたときのミスだろうと思うので。

  • 変数CLSMainとCLSListMain

質問に記載するときのミスなら問題ありませんが、そうでないなら問題です。

vb

1' DoWork内 2 Dim CLSMain As clsMain = New clsMain ’CLSMain 変数を用意 3 lst = CLSListMain.prepare() 'CLSListMainとは?
  • RunWorkerCompleted内でのCancelAsync

If e.Cancelled = True Thenの次の行
これは単純に不要です。ここに入ったときはすでにキャンセルされているので。

その他の問題となりそうなところ

  • Threading.Thread.Sleep(60000)

これだとWhile無いが1回実行されるたびに 1分待つことになります。
現在の問題と関係ないので…と思ってましたが実際に書いてみたら理由がわかった気がします。
これがないと非同期なはずなのに固まるから入れたんではないでしょうか。
もしそうなら固まる理由はReportProgressを呼びすぎていることにあります。
ReportProgressを実行する間は画面側の処理になりますので、これを過剰に呼ぶということは、裏方(別スレッド)に任せたのに、状態を確認しにいって何もできてない状態です。
ウェイトを入れるか、ReportProgressを呼ぶ条件をつけたほうがいいでしょう(カウンタを用意しておいて100回に1回だけ呼ぶなど)。
今は、Thread.Sleep(300)ぐらいにしておけばいいと思います。実際のClsMainがなにしているのかにもよりますが。ここでSleepしている=作業用スレッドは止まっているということに注意してください。

  • DoWorkで使っているlst変数が宣言されていないのと List(Of bean) をnewしすぎ。

結論から言えば、lst( List(Of bean) )のNewはclsMainのprepare()でnewしているので他は必要ありません。
DoWork内では変数を用意すればいいだけです。
今の作りだとWhile Trueのすぐ前の行に用意すればいいかと。

Dim lst As List(Of bean) While True

そして、frmMainの先頭の private lst ~ の行は不要になりますので削除します。
スレッド内(DoWork内)では、スレッド外部で宣言されている変数はあまり触らないようにします(Threadとの連携のために用意したものならいいですが、DoWorkでもfrmMainでも同時に変更したとき厄介だからです)。

  • clsMainでのList(Of bean)の初期化はただしいか。

先ほど、clsMain内だけ List(Of bean) のnewを残しました。
実際の処理次第ではありますが、prepareでListを毎回newしてしまうのでなにか外部のデータでもない限り、その都度Listは空になります。prepare内でアイテムを追加してますので、その回で追加されたアイテム分しか結果としてのこりません。
そこでデータが変わらないならWhileでループしても何回やっても同じになってしまうなと感じました。時刻によってデータが変わったり外部との連携があるなら問題ないのです。
ただ、そうであればそのWhileループの回で、追加するものがなにもなければ結果は 0件になります。


  • キャンセルについて

これは当面の問題が解決してから気にしてください。
中止という意味でキャンセルさせたいとき、DoWork内で e.Cancelと Trueにセットすると、
RunWorkerCompleted内で、CancellationPendingがTrueになります。
キャンセル処理としてはこちらの方が素直な作り方になります。
終了の条件を別途設ける(frmMainのprivate変数にboolean変数を用意しておくとか、RunWorkerAsyncに連携用オブジェクトを渡しておいて、DoWorkEventArgsから受け取る(結果をe.Resultから受け取るように)とか)必要がありますので、今の問題が片付いてから検討してもいいでしょう。
単に終了させるのは、e.CancelにFalseをセットした状態でWhile文を抜ければいいだけです。
キャンセルを終了に流用してるだけなので、そこまで悪いことではないですが、「CancelAsyncよんだけどキャンセルロジックに入らない」というのは後で見るとわかりにくいかもしれません。コメントだけでも残しておくといいでしょう。

投稿2016/08/13 23:09

編集2016/08/14 07:06
flied_onion

総合スコア2604

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2016/08/14 05:17

編集しました。 ・・・まだ処理ができていません。 はじめ、ProgressChangedでUIのテキストボックスに結果を吐き出す処理を書いていましたが、それをProgressChangedから削除して、RunWorkerCompletedに移し替えてみました。 DoWorkでlstに処理して出てきた値を格納して、ProgressChangedでlstに格納されていた値をUIのテキストボックスに吐き出すということを意図していたのですが、デバックで確認するとlstの中に格納した値の受け渡しができてないみたいです。 lstの中身が空になっていました。 timerは存在を知らなかったので、lstの値の受け渡しが解決しましたら実装しようと思います。
flied_onion

2016/08/14 06:12

試してみました。結論から言えば 空ではなかったです(1件入っていました)。 何カ所か気になる点もあったので、回答に追記します。
退会済みユーザー

退会済みユーザー

2016/08/14 13:22

とてもご丁寧な回答ありがとうございます。 backgroundworkerについて仕組みが徐々に理解してきました。 しかしまだ値が取れません(泣) >これは質問用に書き換えたときのミスだろうと思うので。 →はい、質問用に書き換えています。その他の部分も質問用に書き換えています。 無限ループにしているのは目的がサーバー監視のためです。 clsMainの処理中身はその内容になっています。 ちなみにbackgroundworkerを実装していないときはちゃんと値も取れテキストボックスにも記載できていました。
退会済みユーザー

退会済みユーザー

2016/08/15 13:39

解決しました。 基本的にflied_onionさんの教えていただいたことで正しいかったです。 私の質問時に足りない部分があったためでした。 あらかじめtextBox1に入力している値を読み込んで処理を行い、その結果をtextBox2に反映させるということをお伝えしていなかったことが解決できなかった原因でした。 lst = CLSMain.prepare()をbackgroundworkerを起動する前に持って来ることで値が取れるようになりました。 ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問