DOBON.NET DOBON.NETプログラミング掲示板過去ログ

MsgBoxの表示でComboBoxのテキストが消えてしまう

環境/言語:[Windows 7 64bit、VB .NET 2013 EXPRESS、.NET Framework 4.5]
分類:[.NET]

コンボボックスのマウスダウンイベントでメッセージボックスを表示しているのですが、
ホイールボタンでクリックするとコンボボックスの中身が消えてしまいます。
* 左クリックや右クリックの場合は消えません。

消えないようにしたいのですが、どうすればよろしいでしょうか?
よろしくお願いします。


↓ソースファイルです。

Public Class Form1

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load

ComboBox1.DropDownStyle = ComboBoxStyle.DropDownList

ComboBox1.Items.Add("A")
ComboBox1.Items.Add("B")
ComboBox1.Items.Add("C")
ComboBox1.Items.Add("D")
ComboBox1.Items.Add("E")

ComboBox1.SelectedIndex = 2

End Sub

Private Sub ComboBox1_MouseDown(sender As Object, e As EventArgs) Handles ComboBox1.MouseDown

MsgBox("AAA")

End Sub

End Class
実験しましたが、私のところではホイールクリックだけでなく右クリックでもテキストが消えました。
どうして消えるのか私には原因が分かりませんでした。

ただ、他のコントロールを置いて、FormLoad時にComboBox以外にフォーカスがあると
右クリック、中クリックしてもテキストは消えませんでした。

フォームにボタンを追加して、下記のソースで消えなくなりました。

Public Class Form1

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load

        ComboBox1.DropDownStyle = ComboBoxStyle.DropDownList

        ComboBox1.Items.Add("A")
        ComboBox1.Items.Add("B")
        ComboBox1.Items.Add("C")
        ComboBox1.Items.Add("D")
        ComboBox1.Items.Add("E")

        ComboBox1.SelectedIndex = 2
        Me.ActiveControl = Button1

    End Sub

    Private Sub ComboBox1_MouseDown(sender As Object, e As MouseEventArgs) Handles ComboBox1.MouseDown
        MsgBox("AAA")
    End Sub

End Class

また、ActiveControlを設定せず、実行後すぐにボタンに手動でフォーカスを移す操作を
した場合でも消えませんでした。
まりもん様、回答ありがとうございます。


私の環境でも、フォーカスをコンボボックスから
他のコントロールに移したところ、消えなくなりました!

ですがその後、左クリックやタブなどでコンボボックスに
フォーカスを移し、中クリックするとやはり消えてしまいます。。


それならばと思ってコンボボックスのEnterイベントで
他のコントロールにフォーカスを移したところ、
コンボボックスのリストが選択できなくなりました。。。

Private Sub ComboBox1_Enter(sender As Object, e As EventArgs) Handles ComboBox1.Enter

Me.ActiveControl = Button1

End Sub


とりあえず左クリック以外は無効にして回避してみましたが、
スマートな方法はないのでしょうか?
引き続き、よろしくお願いします。

Public Class Form1

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load

ComboBox1.DropDownStyle = ComboBoxStyle.DropDownList

ComboBox1.Items.Add("A")
ComboBox1.Items.Add("B")
ComboBox1.Items.Add("C")
ComboBox1.Items.Add("D")
ComboBox1.Items.Add("E")

ComboBox1.SelectedIndex = 2

End Sub

Private Sub ComboBox1_MouseDown(sender As Object, e As MouseEventArgs) Handles ComboBox1.MouseDown

'左クリック以外無効
If e.Button <> Windows.Forms.MouseButtons.Left Then
Exit Sub
End If

MsgBox("AAA")

End Sub

End Class
MouseClickとかMouseUpのタイミングではだめなのでしょうか?

MouseDownが正しいとしてメッセージボックス出ている間も
マウスのボタンを押しっぱなしなのでしょうか?
shu様、回答ありがとうございます。

はい、MouseUpやMouseClickですと、コンボボックス上で左クリックして、
押したまま移動して他のコントロールで指を離した場合、
MsgBoxを表示した後もコンボボックスのリストが開いたままとなってしまうので、
MouseDownで真っ先に処理したいと考えています。

メッセージボックスが出ている間は、
マウスのボタンを押しっぱなしにしたりはしていません。

よろしくお願いします。
■No32857に返信(りゅうさんの記事)
> MouseDownで真っ先に処理したいと考えています。

MouseDown 中に「ComboBox1.DroppedDown = False」しただけでも
消えてしまうようですし、仕様を見直した方が良いかも知れません。

そもそも何のために、そのような実装を行いたいのでしょうか?

ドロップダウンの最中は、MsgBox を表示したり、項目を書き換えたりすると
予期せぬ動作を引き起こすことを経験しているため(.NET 以前の VB も含む)、
自分の場合は、そうした実装を意図的に避けるようにしています。


> よろしくお願いします。

'案1
Private Async Sub ComboBox1_MouseDown(sender As Object, e As MouseEventArgs) Handles ComboBox1.MouseDown
  Await Task.Run(Sub() MsgBox("AAA"))
End Sub

一応消えなくなりましたが、メッセージボックスがモードレスになるため、
連続してクリックすると、複数の MsgBox が開かれてしまいました。

-----------------

'案2
Private Sub ComboBox1_MouseDown(sender As Object, e As EventArgs) Handles ComboBox1.MouseDown
  Task.Run(Sub() MsgBox("AAA")).Wait()
End Sub

複数の MsgBox が開かれることはなくなりましたが、メッセージ表示中にクリックした場合、
MsgBox が閉じられた後で、次の MsgBox が表示されてしまいました。

-----------------

'案3
Private Alert As EventHandler =
   Sub(sender, e)
    RemoveHandler Application.Idle, Alert
    MsgBox("AAA")
   End Sub

Private Sub ComboBox1_MouseDown(sender As Object, e As MouseEventArgs) Handles ComboBox1.MouseDown
  AddHandler Application.Idle, Alert
End Sub

消える場合もあれば消えない場合もあり、安定しませんでした。

-----------------
'案4
Private Sub ComboBox1_MouseDown(sender As Object, e As MouseEventArgs) Handles ComboBox1.MouseDown
  Dim si = ComboBox1.SelectedIndex
  MsgBox("AAA")
  ComboBox1.SelectedIndex = si
End Sub

メッセージ表示中に消えるものの、直後に復元させています。
消えっぱなしになることを防ぐための妥協案。


-----------------
'案5
Private Declare Function ReleaseCapture Lib "user32" () As <MarshalAs(UnmanagedType.Bool)> Boolean
Private Sub ComboBox1_MouseDown(sender As Object, e As MouseEventArgs) Handles ComboBox1.MouseDown
  ReleaseCapture()
  MsgBox("AAA")
End Sub

すべての環境で回避できるかどうかまでは自信ありません。
魔界の仮面弁士様、多くの案のご提示ありがとうございます。

> ドロップダウンの最中は、MsgBox を表示したり、項目を書き換えたりすると
> 予期せぬ動作を引き起こす
なるほど、ドロップダウンの最中に手を加えるのはよくないんですね。
ありがとうございます。

> 仕様を見直した方が良いかも知れません。
はい、私も仕様を変更したいのですが、諸事情により叶わずです。。

> そもそも何のために、そのような実装を行いたいのでしょうか?
画面の他の項目の入力内容によって、コンボボックスが選択できる・できないといった仕様になっています。
そしてコンボボックスが選択できない時にメッセージを表示して選択をキャンセルさせる、といった動きです。
選択できない場合はEnabledをFalseにでもしたいのですが。。
結果KeyDownイベントでも色々と処理を入れてます。。

案5のReleaseCapture()、こちらの環境でも正しく動作しました!
とりあえずはこの方法で回避したいと思います。

助かりました、ありがとうございます。
■No32861に返信(りゅうさんの記事)
これはどうでしょう?

Public Class ComboBoxEx
    Inherits ComboBox

    Public Event LeftMouseDown As MouseEventHandler

    Protected Overrides Sub OnMouseDown(e As System.Windows.Forms.MouseEventArgs)
        If e.Button = Windows.Forms.MouseButtons.Left Then
            RaiseEvent LeftMouseDown(Me, e)
        Else
            MyBase.OnMouseDown(e)
        End If
    End Sub
End Class



このComboBoxExをComboBoxの変わりに使用して

Public Class Form1

    Private Sub Combo1_LeftMouseDown(sender As System.Object, e As System.Windows.Forms.MouseEventArgs) Handles Combo1.LeftMouseDown
        MessageBox.Show("AAA")
    End Sub
End Class

としてみる。
shu様、2回目の回答ありがとうございます。

コンボボックスを拡張して左クリック専用のイベントを用意し、
他のクリックは従来のマウスダウンイベントをさせる、
といった方法もあるのですね。勉強になります。

ただ1つ気になったのですが、以下のように拡張せずに
if文で処理を行う場合と、どのような違いがあるのでしょうか?

Private Sub ComboBox1_MouseDown(sender As Object, e As MouseEventArgs) Handles ComboBox1.MouseDown
if e.Button = Windows.Forms.MouseButtons.Left Then
'左クリック用の処理
else
'その他のクリック用の処理
End If
End Sub

お時間のある時にでも回答下さると助かります。

DOBON.NET | プログラミング道 | プログラミング掲示板