Страницы: 1 2 След.
RSS
Изменение свойств объектов (Controls) загруженной и показанной на экране UserForm при перемещении указателя курсора мыши по экрану пользователем.
 
Здравствуйте, Уважаемые!

Понимаю, тема изъезженная, однако, блуждая по бесконечным просторам инета, на текущий момент не сумел найти ответа  на вот какой вопрос.
Есть форма (UserForm), напичканная множеством объектов.
Контроль внутри происходящего в форме, как в муравейнике - четко и жестко, мышь не проскользнет.
Но вот засада.
Мерзкий пользователь, работая с формой, вдруг по непонятно каким соображениям перемещает курсор мыши за границы формы.
И вот тут как раз и требуется просигнализировать, так сказать, дать понять исполняемому коду, что нарушена граница, пересечена "точка события", если уж совсем увлекаться в эпитеты, анализируя структуры объектов, аналогичных Черным дырам.
Пусть это даже будет вызов простейшего макроса, содержащего всего лишь вывод на экран предупреждающего сообщения, не важно.
Важно понять как вызвать этот макрос, к какому событию какого объекта формы обратиться.
Я скрупулезно исследовал.
Использование события формы UserForm_MouseMove не помогает.
И даже пытался зайти с другой стороны
Изучил возможности функции API GetCursorPos, определяющей координаты текущего расположения курсора на экране, в результате чего научился дрессировать UserForm, как в цирке на арене, располагая ее где захочу и как захочу.
Пытался даже комбинировать, но безрезультатно.
Чувствую, что решение где-то рядом, но найти пока не могу.
Люди добрые, "поможите" чем можете!  
 
Если форма запущена как модальная, то я с трудом понимаю как нарушены границы.
По вопросам из тем форума, личку не читаю.
 
Владимир Баукин, сформулируйте и предложите новое название темы, из которого будет понятна задача - модераторы поменяют.
 
Цитата
Владимир Баукин написал:
Мерзкий пользователь, работая с формой, вдруг по непонятно каким соображениям перемещает курсор мыши за границы формы.
И что?
 
Предлагаю такой вариант названия темы: "Запуск процедуры (макроса) при перемещении курсора мыши за пределы границ пользовательской формы, показанной на экране".

Цитата
БМВ написал: Если форма запущена как модальная, то я с трудом понимаю как нарушены границы.
Элементарно. Форма загружена, показана пользователю на экране в определенных координатах. Ничто не мешает пользователю, управляя мышью, перемещать курсор по всему экрану. Важно отловить момент, когда курсор, находящийся в пределах координат формы, затем переводится пользователем в координаты, находящимися за пределами этой формы.    
 
Цитата
Владимир Баукин написал:
Мерзкий пользователь, работая с формой, вдруг по непонятно каким соображениям перемещает курсор мыши за границы формы
Владимир Баукин,  Вы #2 читали? Если предложенный вариант не устраивает, поясните для чего нужно
Цитата
Владимир Баукин написал:
найти способ инициировать хоть какую-нибудь процедуру, когда курсор мыши перемещается за границы формы
А к пользователям нужно относиться с любовью :) Может сама форма не очень хороша? ;)

P.S. Пока писал, пояснение появилось.  Владимир, а при чем тут форма? Вы же хотите просто определять положение (координаты) мыши, если правильно понял?
 
Цитата
_Igor_61 написал:
Форма загружена
она нужна не модальная?
По вопросам из тем форума, личку не читаю.
 
Цитата
_Igor_61 написал: Вы же хотите просто определять положение (координаты) мыши
Да в том-то и дело, что координаты я прекрасно научился считывать.
Не получается вот что.
Попробую пояснить простеньким примером.
Предположим, есть пустая форма, в которой расположен, ну, пусть будет 'элементарный Label, при наведении на который цвет фона становится красный.
Теперь пользователь переводит курсор за границы формы. Как только курсор покинет пределы координат формы визуально хочется получить, чтобы цвет фона этого Label стал, например, зеленым.

Цитата
БМВ написал: она нужна не модальная?
Да без разницы какой тип самой формы.  
 
Цитата
Владимир Баукин написал:
"Запуск процедуры (макроса) при перемещении курсора мыши за пределы границ пользовательской формы, показанной на экране".
Такого идиотского странного способа запуска макроса встречать еще не доводилось. 8-0
 
Цитата
Владимир Баукин написал:
хочется получить, чтобы цвет фона этого Label стал, например, зеленым.
суд по всему что просто хочется и все тут. Модельная форма не позволит ничего сделать с приложением за её пределами.
По вопросам из тем форума, личку не читаю.
 
Цитата
Такого идиотского странного способа запуска макроса встречать еще не доводилось

Не вопрос.
Не претендую на безукоризненность.
Если резюме относится именно к формулировке, то можно переформулировать, как "Создание события отслеживания покидания курсором предела пользовательской форме на экране". Если резюме относится к сути самой задачи, то не вижу в ней ничего идиотского.
Более того, именно поэтому и обратился за помощью. Возможно, не спорю, есть идеи и получше. С удовольствием выслушаю рекомендации, предложения.
 
Вы бы озвучили конечную цель - для чего это?
 
Цитата
БМВ написал:
Модельная форма не позволит ничего сделать с приложением за её пределами.
Не сможет и пес с ним. Важно, что пользователь увидит изменения, которые произошли в форме, когда он перевел курсор за ее пределы
 
Ой, кажись понял ;) Нужно контролировать действия пользователей, вносящих изменения в форму и состояние формы в режиме Online  
Изменено: _Igor_61 - 16.05.2021 16:33:49
 
Цитата
vikttur написал:
Вы бы озвучили конечную цель - для чего это?
Цель - визуальное предоставление пользователю состояния формы при нахождении курсора вне ее координат
 
Я пользователь. Нахрена мне
Цитата
Владимир Баукин написал:
визуальное предоставление пользователю состояния формы при нахождении курсора вне ее координат
и как оно выглядит?
Изменено: RAN - 16.05.2021 16:40:06
 
Цитата
Владимир Баукин написал:
визуальное предоставление пользователю состояния формы при нахождении курсора вне ее координат
Владимир, состояние форме задает либо текущий пользователь, либо тот, кто контролирует эту форму независимо от других пользователей. Если Вы уже умеете определять координаты мыши, как говорите, о чем вопрос? Это Вы так просто количество сообщений набираете?
 
Есть, конечно, одна идея.
Например, обрамить границы формы объектами Label.
Тогда в любых вариантах передвижения курсора пользователем, используя событие MoseMove этих пограничных объектов, приводить вид объектов внутри формы к задуманному состоянию, которое хочется визуально получить, когда курсор переводится за границы формы.
Но может есть предложения поинтереснее?

Цитата
_Igor_61 написал: Это Вы так просто количество сообщений набираете?
Я бы с радостью перешел скорее к реализации следующего этапа своей задачи, решив эту.
Но проблема этого этапа пока не решена.
Да, я знаю координаты формы, могу определить координаты курсора.
Но не понимаю пока, как это объединить воедино, чтобы как только курсор получил координаты, находящимися за пределами границы формы произошел бы вызов какой-либо процедуры, которая бы изменила визуальное представление объектов формы
 
По-видимому, речь идет о курсоре мыши.

Проведите эксперимент. Создайте форму с одним текстовым полем, запустите на выполнение и уведите курсор мыши далеко от формы. Начните вводить текст с клавиатуры. Вы видите курсор (клавиатуры) в тексте. Далее, сделайте движение мышью. Курсор мыши как был далеко от формы, так и остался.
На поведение (модальной) формы никак не влияют движения мыши вне этой формы. Когда мышь пересекает границу формы начинает срабатывать событие формы (или ее элементов управления) MouseMove.
Владимир
 
Цитата
RAN написал:
и как оно выглядит?
Курсор внутри формы - объект формы красный.
Курсор вне формы - объект формы зеленый  
 
нет такого события и обрамления вам не помогут, так как быстрое перемещение не вызовет событие. Остается крутить по таймеру получение позиции курсора и сравнение с координатами формы.
По вопросам из тем форума, личку не читаю.
 
Код  ZVI отсюда вам помощь.
 
Цитата
Владимир Баукин написал:
обрамить границы формы объектами Label.
И сколько их потребуется? Если в форме больше хотя бы трех элементов управления? А через месяц понадобится добавить в форму еще что-то... Следите за мышью ;)
 
Есть ещё один момент: ведь при загрузке формы указатель мыхи может находиться где угодно. И как тут быть?
 
Хороший вопрос... Еще сильнее озадачил

Цитата
_Igor_61 написал: И сколько их потребуется?
всего 4

Цитата
БМВ написал:  так как быстрое перемещение не вызовет событие
Спасибо за замечание, учту!
 
Решение в принципе есть. На основе варианта от ZVI: подогнать размер формы (или размеры ячеек) таким образом, чтобы границы формы совпадали с границей некого диапазона. И контролировать выход за пределы ЭТОГО диапазона. Но остаётся проблема со стартовой позицией указателя мыхи. Я не знаю, как его принудительно установить в нужное место.
 
Вот набросок.
 
Снова всем здравия!

Тема по-прежнему для меня актуальна.

Вниманию модераторов!
Прошу переформулировать тему в окончательный правильный вариант:
Изменение свойств объектов (Controls) загруженной и показанной на экране пользовательской формы (UserForm) при перемещении указателя курсора мыши по экрану пользователем.

Предварительно отдельно хочу поблагодарить БМВ, что не позволил сойти с пути истинного.
На этом лирику закончим и перейдем к конкретике.

Никак не могу найти ошибку в своем коде  в силу недостаточности уровня знаний в понимании правил использования обратного вызова с использованием API функций.
Начнем по порядку.
Итак, есть задача - изменять для визуальной наглядности пользователю состояние свойств объектов (Controls) загруженной и выведенной на экран формы (UserForm) при перемещении пользователем мыши по экрану окна.
Возьмем для простоты простейшую форму, содержащую объект Image.
Желаемое:
Если пользователь навел курсор мыши на форму (курсор мыши внутри координат формы), то фон Image зеленый.
Если пользователь перевел курсор мыши за пределы координат формы , то фон Image становится красный.
Все это я пытаюсь реализовать в 64-bit операционной системе с установленной версией Office, использующего 64-разрядную версию VBA.
Создавая различного рода запросы в поисковике для решения своей задачи я выяснил, что в помощь мне функция API TRACKMOUSEEVENT.
И даже нашел практический пример по ссылке: http://rusproject.narod.ru/winapi/t/trackmouseevent.html
Изучив его, я понял, что мне его необходимо адаптировать в свою операционную среду.
И тут снова мне в помощь пришел найденный справочный материал:  https://codekabinett.com/rdumps.php?Lang=2&targetDoc=windows-api-declaration-vba-64-bit
Изучив на его основании типы аргументов необходимых мне API функций,  я адаптировал их декларацию под версию VBA 64-bit.
При проверке компиляции кода в среде VBA.Project все хорошо.
Однако результат выполнения кода не дает нужного результата.
Более того, при пошаговом выполнении наблюдается серьезный сбой, когда приложение Excel  просто перезагружается или даже закрывается.
И это происходит именно на этапе срабатывания функции обратного вызова.
Теперь конкретно о коде.

Вот код основного модуля
Код
Option Explicit

Public Const WM_MOUSELEAVE As Long = &H2A3&
Public Declare PtrSafe Function TRACKMOUSEEVENT Lib "user32" Alias "TrackMouseEvent" ( _
          lpEventTrack As TRACKMOUSEEVENT) As Long

Public Type TRACKMOUSEEVENT
    cbSize As Long
    dwFlags As Long
    hwndTrack As LongPtr
    dwHoverTime As Long
End Type

Public Const TME_LEAVE As Long = &H2

Declare PtrSafe Function SetWindowLong Lib "user32" Alias "SetWindowLongPtrA" (ByVal hWnd As LongPtr, ByVal nIndex As Long, ByVal dwNewLong As LongPtr) As LongPtr

Declare PtrSafe Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As LongPtr, ByVal hWnd As LongPtr, ByVal Msg As Long, ByVal wParam As LongPtr, ByVal lParam As LongPtr) As LongPtr

Private Const GWLP_WNDPROC = (-4)
Dim PrevProc As LongPtr
Public Track As TRACKMOUSEEVENT
Public NoRecursForm As Boolean

Public Sub Hook(ByVal frmHWnd As LongPtr)
    PrevProc = SetWindowLong(frmHWnd, GWLP_WNDPROC, AddressOf WindowProc)
End Sub

Public Sub UnHook(ByVal frmHWnd As LongPtr)
    SetWindowLong frmHWnd, GWLP_WNDPROC, PrevProc
End Sub

Public Function WindowProc(ByVal hWnd As LongPtr, ByVal uMsg As Long, ByVal wParam As LongPtr, ByVal lParam As LongPtr) As LongPtr
    
    If uMsg = WM_MOUSELEAVE Then

        If UserForm1.Image1.BackColor = vbGreen Then
           UserForm1.Image1.BackColor = vbRed
        Else
           UserForm1.Image1.BackColor = vbGreen
        End If
        
    End If
    
    WindowProc = CallWindowProc(PrevProc, hWnd, uMsg, wParam, lParam)
End Function

Sub Example()
    Load UserForm1
    UserForm1.Show
End Sub

В процедуре Hook основного модуля используется параметр дескриптора окна пользовательской формы frmHwnd.
Я знаю, что у UserForm и его Controls нет свойства hwnd, поэтому мне опять же в помощь пришел справочный материал: https://colinlegg.wordpress.com/2016/05/06/getting-a-handle-on-userforms-vba/, изучив который я научился считывать дескриптор пользовательской формы.

Соответственно, код класса оговоренной для примера формы приобрел следующий вид:
Код
Option Explicit

Private Declare PtrSafe Function FindWindowA Lib "user32.dll" (ByVal lpClassName As String, ByVal lpWindowName As String) As LongPtr
 
Private mlnghWnd As LongPtr
 
Public Property Get hWnd() As LongPtr
    hWnd = mlnghWnd
End Property
 

Private Sub UserForm_Initialize()
   StorehWnd
   Hook Me.hWnd
   With Track
       .cbSize = Len(Track)
       .dwFlags = TME_LEAVE
       .hwndTrack = Me.hWnd
       .dwHoverTime = 400
   End With
End Sub
 
Private Sub StorehWnd()
 
    Dim strCaption As String
    Dim strClass As String
 
    'class name changed in Office 2000
    If Val(Application.Version) >= 9 Then
        strClass = "ThunderDFrame"
    Else
        strClass = "ThunderXFrame"
    End If
 
    'remember the caption so we can
    'restore it when we're done
    strCaption = Me.Caption
 
    'give the userform a random
    'unique caption so we can reliably
    'get a handle to its window
    Randomize
    Me.Caption = CStr(Rnd)
 
    'store the handle so we can use
    'it for the userform's lifetime
    mlnghWnd = FindWindowA(strClass, Me.Caption)
 
    'set the caption back again
    Me.Caption = strCaption
 
End Sub

Private Sub UserForm_MouseMove(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
    TRACKMOUSEEVENT Track
End Sub

Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
    UnHook Me.hWnd
End Sub

Однако не работает(
При пошаговом исполнении при обращении к функции WindowProc, я заметил, что параметр uMsg вообще даже близко не сопоставляется с установленной константой WM_MOUSELEAVE.
При этом происходит рекурсивное обращение к указанной функции, параметр uMsg меняется каждый раз, а после раза 4-5 рекурсивного вызова, приложение просто перезапускается или выгружается.

Уважаемые знатоки темы, подскажите, где у меня ошибка и/или чего мне не хватает (кроме ума и знаний, тут, надеюсь, понятно, шутки будут неуместны).  
Изменено: Владимир Баукин - 22.05.2021 03:25:42
 
Всех приветствую!

Мда... Не удалось победить модальный вариант формы даже с помощью API функции TrackMouseEvent.
Раскопал в загашнике 32-х битную версию винды, посмотрел, как работает эта функция на примере, упомянутом в ссылке вышеизложенного поста и понял, что все равно, как не трудись, а все упирается в событие MouseMove, которое при определенных ситуациях срабатывает, к сожалению, далеко не всегда.
Удалось это даже реализовать "костыльным" методом в 64-х разрядной операционной среде
Однако не сожалению о приобретенных знаниях, изучая справочные материалы, к адаптации API функций к 64-bit системе.
Задачку все-таки удалось решить, то есть достигнуть желаемого результата пусть и не совсем так, как это изложено в теме.
Сосредоточился на использование немодального варианта UserForm.
Если в листе книги Excel вызывается подобного рода форма и этот лист не обременен необходимостью обрабатывать свои события, то решение простенькое, наглядное, с которым можно ознакомиться в прилагаемом примере (Пример 1.zip)
Другое дело, если в листе книги Excel, в котором вызывается форма, задействуются события, такие как, например, SelectionChange.
В этом случае, особенно если для последующих вычислений, переадресации результирующих данных активного листа книги в другие области, организации ссылок и т.п. используются данные из вызванной формы, которые изменяют свойства объектов активного листа, то применение немодальной формы, когда возможно прямое изменение свойств объектов листа  книги Excel пользователем  в обход вызванного на экран диалогового окна, чревато непредсказуемыми последствиями.
Соответственно, пользователя нужно попытаться оградить от случайных "нежданчиков", когда у него случайно дернется рука, находящаяся на мышке, или просто обернувшись, случайно заденет мышь локтем, чтобы он визуально увидел, что диалог прервался. Много различных вариантов можно придумать
Либо, если это злоумышленник, то не позволить ему совершить свое коварное дело изменить результат путем ручного (не через форму) изменения ключевых данных листа книги Excel
Но машине-то по фигу, она реагирует на событие, созданное пользователем.
Поэтому, в результате упорных поисков и экспериментов, пришел к альтернативному варианту использования немодальной формы и принудительного контроля курсора мыши.
При этом управление курсором мыши я захватываю уже на этапе загрузки формы.
Желающие могут ознакомиться с решением в прилагаемом файле Пример 2.zip

Всем спасибо за внимание!
С Уважением
Изменено: Владимир Баукин - 29.05.2021 23:54:26
 
см.вложение
Программисты - это люди, решающие проблемы, о существовании которых Вы не подозревали, методами, которых Вы не понимаете!
Страницы: 1 2 След.
Наверх