Как правильно сделать кнопочку с событием MouseLeave.

Разговоры на любые темы: вы можете обсудить здесь какой-либо сайт, найти единомышленников или просто пообщаться...
ANDLL
Великий гастроном
Великий гастроном
Аватара пользователя
 
Сообщения: 3450
Зарегистрирован: 29.06.2003 (Вс) 18:55

Как правильно сделать кнопочку с событием MouseLeave.

Сообщение ANDLL » 01.01.2007 (Пн) 16:42

Для начала, как водится, вкратце опишу задачу.
Итак, на форму помещается некоторый элемент управления, скажем, Label или CommandButton. Часто требуется что бы он как-то реагировал на наведение курсора на себя любимого. Скажем, менял цвет или иконку на себе, тем самым сигнализируя пользователю что это именно работающая кнопка а не что-то там еще.
С наведением курсора просто - при этом генерируется событие MouseMove его мы и поймаем. А как быть с моментом ухода(leave) курсора с нашего контрола? События MouseLeave в VB как известно нету.

Для решения этого вопроса мы и рассмотрим пару методов в порядке убывания их кривости(на мой взгляд):

1. Ловить событие MouseMove на контейнере.
Тут все просто, если кнопочка размещена скажем на форме, то в тот момент когда курсором водят по форме, очевидно, им уже не водят по самой кнопке. Поэтому, достаточно написать простой код и мигающая кнопка готова!
Код: Выделить всё
Private Sub Command1_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
    Command1.FontItalic = True
End Sub

Private Sub Form_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
    Command1.FontItalic = False
End Sub

Минусы этого кода перевешивают все плюсы простоты. Достаточно перенести кнопку в какой-нибудь угол и наша метода тут же перестает работать, не говоря уже о многих других минусах. Именно поэтому в ряду кривости этот метод занимает почетное первое место.

2. Сабклассинг события WM_MOUSELEAVE.
Вот так оказия, хоть в VB6 события MouseLeave и нету, тем не менее в windows есть сообщение WM_MOUSELEAVE. Совершенно очевидно, что если это окно засабклассить, то можно ловить это сообщение(правда на него еще надо подписаться с помощью TrackMouseEvent, но это уже другая история) и легко сделать такую же "мигающую" кнопку. Я не буду приводить код этого способа, сабклассингу посвящено много статей. Могу лишь пожелать людям, которые изберут этот метод удачи при отладке своих приложений. Ибо она им пригодится.

3. Таймер и сравнение rect'ов.
Тут то же все просто. В событии MouseMove мы запускаем некоторый таймер, который с регулярностью в 100 мс проверяет, не ушел ли курсор за пределы прямоугольника нашего окна. Если ушел - пора гасить свечи. Код то же простой:
Код: Выделить всё
Option Explicit

Private Declare Function GetWindowRect Lib "user32" (ByVal hwnd As Long, lpRect As RECT) As Long
Private Declare Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As Long
Private Type RECT
        Left As Long
        Top As Long
        Right As Long
        Bottom As Long
End Type
Private Type POINTAPI
        X As Long
        Y As Long
End Type

Private Sub Command1_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
    If Not Timer1.Enabled Then
        Timer1.Enabled = True
        Command1.FontItalic = True
    End If
End Sub

Private Sub Timer1_Timer()
    Dim rc As RECT, pt As POINTAPI
    GetWindowRect Command1.hwnd, rc
    GetCursorPos pt
    If pt.X < rc.Left Or pt.X > rc.Right Or pt.Y < rc.Top Or pt.Y > rc.Bottom Then
        Timer1.Enabled = False
        Command1.FontItalic = False
    End If
End Sub

Плюсы этого способа очевидны: он работает где-бы не располагался наш контрол(на любом контейнере и в любой его области). Минусов у этого способа на самом деле нету, если не считать таймера. Он видите ли некоторым не нравится. Ну, для них есть еще один метод.

4. Захват мыши.
Захват(Capture) мыши заключается в том, что windows будет посылать события MouseMove нашему окну даже тогда, когда курсор покинет его приделы (иными словами до тех пор, пока мы ее не отпустим). Код еще короче, чем в прошлый раз:
Код: Выделить всё
Private Declare Function SetCapture Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function ReleaseCapture Lib "user32" () As Long

Private Sub Command1_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
    Static bCaptured As Boolean
    If bCaptured Then
        If X > Command1.Width Or Y > Command1.Height Or X < 0 Or Y < 0 Then
            ReleaseCapture
            bCaptured = False
            Command1.FontItalic = False
        End If
    Else
        Command1.FontItalic = True
        bCaptured = True
        SetCapture Command1.hwnd
    End If
End Sub
Думаю, в комментариях этот код не нуждается.

На последок отмечу, что последний способ - мой любимый, хотя бы потому, что он краток и удобен в использовании и для него не надо еще одного контрола на форме.

Ну и конечно же, всех с Новым Годом!
Последний раз редактировалось ANDLL 01.01.2007 (Пн) 17:25, всего редактировалось 1 раз.
Гастрономия - наука о пище, о ее приготовлении, употреблении, переварении и испражнении.
Блог

keks-n
Доктор VB наук
Доктор VB наук
Аватара пользователя
 
Сообщения: 2509
Зарегистрирован: 19.09.2005 (Пн) 17:17
Откуда: г. Москва

Сообщение keks-n » 01.01.2007 (Пн) 17:02

С контейнером не катит точно. При определённых условиях может не произойти события. Сабклассинг может помешать отладке, хотя есть хитрый сабклассер на основе асмовой вставки, который позволяет тормозить программу без падения среды. Таймер - некрасиво и неэкономично. Capture - лучший способ.
Изображение

GSerg
Шаман
Шаман
 
Сообщения: 14286
Зарегистрирован: 14.12.2002 (Сб) 5:25
Откуда: Магадан

Сообщение GSerg » 01.01.2007 (Пн) 18:21

When the mouse is captured, menu hotkeys and other keyboard accelerators do not work.


А правильный способ - WM_MOUSELEAVE.
Как только вы переберёте все варианты решения и не найдёте нужного, тут же обнаружится решение, простое и очевидное для всех, кроме вас

ANDLL
Великий гастроном
Великий гастроном
Аватара пользователя
 
Сообщения: 3450
Зарегистрирован: 29.06.2003 (Вс) 18:55

Сообщение ANDLL » 03.01.2007 (Ср) 11:37

GSerg писал(а):
When the mouse is captured, menu hotkeys and other keyboard accelerators do not work.


А правильный способ - WM_MOUSELEAVE.
Windows Media Player 9, видимо, использует способ 3.
Он не использует WM_MOUSELEAVE, по крайней мере, Spy++ этого сообшения там не видит.
Иными словами, не надо возводить свое мнение в ранг "правил".
Гастрономия - наука о пище, о ее приготовлении, употреблении, переварении и испражнении.
Блог

gaidar
System Debugger
System Debugger
 
Сообщения: 3152
Зарегистрирован: 23.12.2001 (Вс) 13:22

Сообщение gaidar » 03.01.2007 (Ср) 12:32

ANDLL - пришли статью в Word на gaidar@vbstreets.ru. Опубликуем в книге.
The difficult I’ll do right now. The impossible will take a little while. (c) US engineers in WWII
I don't always know what I'm talking about, but I know I'm right. (c) Muhammad Ali

ANDLL
Великий гастроном
Великий гастроном
Аватара пользователя
 
Сообщения: 3450
Зарегистрирован: 29.06.2003 (Вс) 18:55

Сообщение ANDLL » 03.01.2007 (Ср) 12:52

gaidar
Я уже присылал тебе одну статью, для сайта, ты ее не опубликовал и на письмо не ответил. Почему?
Эту статью я то же отправил.
Если вдруг и она не дойдет, напиши здесь или ЛС.
Гастрономия - наука о пище, о ее приготовлении, употреблении, переварении и испражнении.
Блог


Вернуться в Народный треп

Кто сейчас на конференции

Сейчас этот форум просматривают: Google-бот и гости: 116

    TopList