この記事では、Visual Basic NET で、イベントハンドラを実行しているとき、あたらしく同じイベントが発生して、何度もイベントハンドラが実行されないようにする方法を説明します。
このブログでは、いままで、VB.NETのことは書いてなかったんですが、ネットサーフィンしてもなかなか見つからなかったため、書いておきます。
vb.net で イベントハンドラ実行中に新しくイベント発生しないようにする方法
例えば、というか、僕の例ですが、データグリッドビューでセルを変更したとき、CellValueChanged イベントを発生させて処理してます。処理の中で、違うセルに値を代入してるのですが、セルに値を代入した時点でさらにCellValueChangedイベントが発生して、ハンドラが2重、3重で実行されてしまうという事態が発生してしまいました。
開発環境ですが、Visual Basic Community 2019、Visual Basic、 .NET Frameworkは昔のままで4.5を使ってます。でも、バージョンによらず普遍的な話だと思います。
CellValueChangedのイベントハンドラ該当箇所
下のリストは、修正後のCellValueChangedイベントハンドラの処理です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
'セルの編集終了 Private Sub dgv_setting_CellValueChanged(sender As Object, e As DataGridViewCellEventArgs) Handles dgv_setting.CellValueChanged Me.write_to_setting() End Sub '設定の反映 Private Sub write_to_setting() If Not IsNothing(Me.sts_work_setting) Then Me.sts_work_setting.clear() If Me.dgv_setting.Rows.Count > 0 Then For i As Integer = 0 To Me.dgv_setting.Rows.Count - 1 With Me.dgv_setting.Rows(i) Dim str_menu As String = CStr(.Cells(enm_clmn_order.IDX_MENU).Value) Dim str_menu_in As String = CStr(.Cells(enm_clmn_order.IDX_MENU_IN).Value) Dim str_sts_work As String = CStr(.Cells(enm_clmn_order.IDX_LOG).Value) Dim str_color As String = CStr(.Cells(enm_clmn_order.IDX_COLOR).Value) 'メニュー表示を入力したとき、終了表示、ログ表示がなかったら、メニュー表示をもとに自動入力 If str_menu <> "" Then '割り込み禁止 'write_to_settingでセル編集しているため RemoveHandler Me.dgv_setting.CellValueChanged, AddressOf dgv_setting_CellValueChanged '終了表示 自動入力 If str_menu_in = "" Then str_menu_in = str_menu & "終了" .Cells(enm_clmn_order.IDX_MENU_IN).Value = str_menu_in End If 'ログ表示 自動入力 If str_sts_work = "" Then str_sts_work = str_menu .Cells(enm_clmn_order.IDX_LOG).Value = str_sts_work End If AddHandler Me.dgv_setting.CellValueChanged, AddressOf dgv_setting_CellValueChanged '色自動入力 If str_color = "" Then str_color = "Cyan" '色指定忘れ End If Me.sts_work_setting.add_setting(CBool(.Cells(enm_clmn_order.IDX_F_VALID).Value), str_color, CBool(.Cells(enm_clmn_order.IDX_F_AT_WORK).Value), str_menu, str_menu_in, str_sts_work) End If End With Next End If End If End Sub |
簡単にいうと、データグリッドビューで3列目に値を入力したら、4列目と5列目に自動でテキストを入力するという処理をしています。
ちなみに下図のようなデータグリッドビューの話です。メニュー表示に入力したら、終了表示とログ表示に自動で値を入れるようにしてます。
イベントを発生させないようにする処理
上のリストの中で該当部分を下リストに挙げます。
1 2 3 4 5 6 7 8 9 10 11 12 |
RemoveHandler Me.dgv_setting.CellValueChanged, AddressOf dgv_setting_CellValueChanged '終了表示 自動入力 If str_menu_in = "" Then str_menu_in = str_menu & "終了" .Cells(enm_clmn_order.IDX_MENU_IN).Value = str_menu_in End If 'ログ表示 自動入力 If str_sts_work = "" Then str_sts_work = str_menu .Cells(enm_clmn_order.IDX_LOG).Value = str_sts_work End If AddHandler Me.dgv_setting.CellValueChanged, AddressOf dgv_setting_CellValueChanged |
RemoveHandler で、いったん ハンドラ関数を外して、セルへの代入が終わったら、もう一回、AddHandler で同じ関数を登録してます。
今回の目的は、ハンドラ関数のコード中での代入によってイベント発生させないことだったので、最低限のマスク期間としました。
RemoveHandler、AddHandler の構文はどちらも同じで、
1 |
RemoveHandler コントロール.イベント名, AddressOf 関数名 |
という順番に書きます。
僕の例の場合は、
1 |
RemoveHandler Me.dgv_setting.CellValueChanged, AddressOf dgv_setting_CellValueChanged |
という形で書いてます。
組み込み系のように割り込みマスク処理はなさそう
僕は長らく、組み込み系をやって、具体的には自動車のエンジン制御やモータ制御をやってたんですが、割り込み処理が入ると、とりあえず、その処理が終わるまでの間、同じ割込みが発生しないように処理します。
具体的には、CPUの割り込み制御で割り込みが発生しないように設定を切り替えたりするんです。
それに比べれば、ちょっと大掛かりな気がしますし、もっと簡単に、組み込みの時みたいな簡単な記述方法もあるのかなって思います。
でも、とりあえず、この方法で問題なくできてるんでまあ良しとしましょう。
まとめ
VB.NET でデータグリッドビューのCellValueChanged イベントの処理を例として、イベントハンドラ内で、イベントハンドラの多重呼び出しを防ぐ方法を紹介しました。
他にもやり方あると思うのですが、イベントハンドラ内で、RemoveHandler を使って割り込みが入らないようにして、処理終了後は、AddHandler で再登録するというやり方を紹介してます。
ざっと、検索しても、C#の記事は出てきても、今回、VB.netの記事が出てこなかったのと、かつ、割り込み禁止にする処理も、すんなり出てこなかったため、書いてみました。
参考になったら幸いです。
コメント