投稿者 魔界の仮面弁士  (社会人) 投稿日時 2022/9/7 21:34:34
代替策として、Timer 等で継続的にマウス位置を取得し、
TableLayoutPanel1の内外に移動したかどうかを、その都度判定するとか。

Private Sub Form1_Panel1MouseEnter(sender As Object, e As EventArgs) Handles Me.Panel1MouseEnter

End Sub

Private Sub Form1_Panel1MouseLeave(sender As Object, e As EventArgs) Handles Me.Panel1MouseLeave

End Sub


Private Event Panel1MouseEnter As EventHandler
Private Event Panel1MouseLeave As EventHandler
Private inside As Boolean = False
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    Timer1.Interval = 50
    Timer1.Start()
End Sub
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
    Dim p = MousePosition
    Dim cp = TableLayoutPanel1.Parent.PointToClient(p)
    If TableLayoutPanel1.Bounds.Contains(cp) Then
        If Not inside Then
            inside = True
            RaiseEvent Panel1MouseEnter(TableLayoutPanel1, EventArgs.Empty)
        End If
    Else
        If inside Then
            inside = False
            RaiseEvent Panel1MouseLeave(TableLayoutPanel1, EventArgs.Empty)
        End If
    End If
End Sub


なお上記は、TableLayoutPanel1 の一部が他のコントロールなどによって
隠されていた場合のことは考慮されていません。


> MouseEnterはそれっぽく実現できましたが
その方法をとる場合、もしも Button1 の周囲に隙間があれば、
Button1_MouseEnter と共に TableLayoutPanel1_MouseEnter が発生することになります。
場合によっては、その分の補正も必要になるかもしれませんね。


> TableLayoutPanel1の中にマウスカーソルが入ったときにイベントが欲しいのですが、
> Button1上にマウスカーソルが来たときに
> TableLayoutPanel1_MouseLeave
> Button1_MouseEnter
> の2つが呼ばれてしまいます。

マウス操作は基本的に、最も手前にあるウィンドウによってキャプチャされる仕様です。
ただしドラッグ操作の場合は、ドラッグを開始したウィンドウによってキャプチャされます。
https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.forms.control.capture

Button クラスの WndProc メソッド(≠Form クラスの WndProc ではありません)を
オーバーライドすれば、マウス操作を透過させて背面のウィンドウに
キャプチャさせることもできます。

この方法をとれば、マウス操作を常に親コントロール側のイベントでとらえられますし、
タイマー等で監視する必要もなくなります。

Protected Overrides Sub WndProc(ByRef m As Message)
    Const WM_NCHITTEST As Integer = &H84
    Const HTTRANSPARENT As Integer = -1
    If m.Msg = WM_NCHITTEST Then
        m.Result = New IntPtr(HTTRANSPARENT)
    Else
        MyBase.WndProc(m)
    End If
End Sub


ただしマウス操作が透過するということは、そのボタンをクリックすることさえも
できなくなるという事を意味します。(キーボードで操作することは可能ですが)

そのため、親となる TableLayoutPanel 側でクリックを受け取って
代わりに処理するといった対策が必要になるでしょう。

Private Sub TableLayoutPanel1_MouseClick(sender As Object, e As MouseEventArgs) Handles TableLayoutPanel1.MouseClick
    Dim b = TryCast(TableLayoutPanel1.GetChildAtPoint(e.Location), Button)
    If b Is Button1 AndAlso b.Visible AndAlso b.Enabled Then
        If Not b.Focused Then
            b.Focus()
        End If
        b.PerformClick()
    End If
End Sub