Событие Listview MouseLeave: заголовок не включен - PullRequest
2 голосов
/ 31 марта 2020

Я подписался на событие MouseLeave моего ListView. Событие должно возникать, когда указатель мыши покидает границы ListView.

Это работает, но когда указатель мыши входит в заголовок ListView и затем покидает границы ListView, событие не вызывается.

Private Sub LV1_test_MouseLeave(sender As Object, e As EventArgs) Handles LV1_test.MouseLeave
    // Not raised when the Pointer leaves the premises from the top of the ListView
End Sub

Что я могу сделать?

1 Ответ

2 голосов
/ 31 марта 2020

Заголовок ListView на самом деле является другим объектом, его имя класса SysHeader32.
Заголовок отображается в подробном представлении, но он создается вместе с ListView, поэтому он есть, даже если вы его не видите (если Вы добавили хотя бы одну колонку, то есть).

Это не управляемый дочерний элемент управления ListView: коллекция ListView.Controls обычно пуста.
Но это дочерний элемент управления SysListView32, из которого происходит управляемый класс, таким образом, вы может получить его дескриптор и прочитать его сообщения; сообщение WM_MOUSELEAVE, в данном случае.

  • Мы можем получить его дескриптор, используя FindWinDowEx или SendMessage LVM_GETHEADER), назначить дескриптор для * 1023 Класс * NativeWindow переопределяет его WndProc и , перехватывает сообщения, которые мы должны обработать. В WM_MOUSELEAVE класс NativeWindow вызывает событие, на которое может подписаться родительский ListView, в результате чего возникает собственное событие MouseLeave.

Поскольку, как описано, заголовок является отдельным объектом, ListView генерирует событие MouseLeave, когда указатель мыши перемещается над его заголовком. Нам нужно переопределить это поведение, поэтому событие MouseLeave возникает только тогда, когда указатель мыши полностью покидает границы ListView.

  • Мы можем переопределить OnMouseLeave, проверить, попадает ли позиция, возвращаемая MousePosition (в пересчете на меры клиента), в пределы клиента ListView и позволить методу поднять MouseLeave событие только тогда, когда это не так.

РЕДАКТИРОВАТЬ :
Добавлена ​​WM_PARENTNOTIFY проверка сообщения (для уведомления о событии WM_CREATE) для обработки Создание заголовка во время выполнения.


Пользовательский элемент управления ListView:

Теперь, если вы подпишетесь на событие MouseLeave этого пользовательского элемента управления, событие возникает только тогда, когда указатель мыши покидает клиентскую область ListView, независимо от того, где находится курсор.

Imports System.ComponentModel
Imports System.Runtime.InteropServices
Imports System.Windows.Forms

<DesignerCategory("Code")>
Class ListViewCustom
    Inherits ListView

    Private Const LVM_GETHEADER As Integer = &H1000 + 31

    <DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)>
    Friend Shared Function SendMessage(hWnd As IntPtr, uMsg As Integer, wParam As IntPtr, lParam As IntPtr) As IntPtr
    End Function

    Private sysHeader As SysHeader32 = Nothing

    Private Sub AddSysHeaderHandler()
        If DesignMode Then Return
        If sysHeader Is Nothing Then
            Dim sysHeaderHwnd = SendMessage(Me.Handle, LVM_GETHEADER, IntPtr.Zero, IntPtr.Zero)
            If sysHeaderHwnd <> IntPtr.Zero Then
                sysHeader = New SysHeader32(sysHeaderHwnd)
                AddHandler sysHeader.SysHeaderMouseLeave,
                    Sub(s, evt)
                        Me.OnMouseLeave(evt)
                    End Sub
            End If
        End If
    End Sub

    Protected Overrides Sub OnHandleCreated(e As EventArgs)
        MyBase.OnHandleCreated(e)
        AddSysHeaderHandler()
    End Sub

    Protected Overrides Sub OnMouseLeave(e As EventArgs)
        If Not Me.ClientRectangle.Contains(PointToClient(MousePosition)) Then
            MyBase.OnMouseLeave(e)
        End If
    End Sub

    ' Handles the Header creation at run-time
    Protected Overrides Sub WndProc(ByRef m As Message)
        Select Case m.Msg
            Case &H210 'WM_PARENTNOTIFY
                Dim msg As Integer = m.WParam.ToInt32() And &HFFFF
                Select Case msg
                    Case &H1 ' WM_CREATE
                        AddSysHeaderHandler()
                End Select
        End Select
        MyBase.WndProc(m)
    End Sub

    Protected Overrides Sub Dispose(disposing As Boolean)
        If (disposing) Then sysHeader?.ReleaseHandle()
        MyBase.Dispose(disposing)
    End Sub

    Private Class SysHeader32
        Inherits NativeWindow

        Public Event SysHeaderMouseLeave As EventHandler(Of EventArgs)

        Public Sub New(handle As IntPtr)
            AssignHandle(handle)
        End Sub

        Protected Friend Overridable Sub OnSysHeaderMouseLeave(e As EventArgs)
            RaiseEvent SysHeaderMouseLeave(Me, e)
        End Sub

        Protected Overrides Sub WndProc(ByRef m As Message)
            Select Case m.Msg
                Case &H2A3 'WM_MOUSELEAVE
                    OnSysHeaderMouseLeave(EventArgs.Empty)
                    m.Result = IntPtr.Zero
                    Exit Select
                Case Else
                    ' NOP: Log other messages, add more cases...
            End Select
            MyBase.WndProc(m)
        End Sub
    End Class
End Class
...