Nano Hash - криптовалюты, майнинг, программирование

Получение WM_MOUSEMOVE от дочерних элементов управления в MFC CDialog

У меня есть диалог, полученный из CDialog, и я хочу закрыть его, как только пользователь переместит курсор мыши от него. Для этого я добавил обработчик OnMouseLeave, который вызывает OnCancel (). Насколько я понимаю, для своевременной отправки событий WM_MOUSELEAVE необходимо вызвать TrackMouseEvent внутри подпрограммы OnMouseMove. Итак, весь код выглядит следующим образом:

void CDlgMain::OnMouseLeave()
{
 CDialog::OnMouseLeave();

 // Close dialog when cursor is going out of it
 OnCancel();
}

void CDlgMain::OnMouseMove(UINT nFlags, CPoint point)
{
 TRACKMOUSEEVENT tme;
 tme.cbSize = sizeof(tme);
 tme.hwndTrack = m_hWnd;
 tme.dwFlags = TME_LEAVE;
 tme.dwHoverTime = HOVER_DEFAULT;
 TrackMouseEvent(&tme);

 CDialog::OnMouseMove(nFlags, point);
}

Он работает нормально, но диалоговое окно закрывается, когда пользователь наводит курсор на некоторые из его дочерних элементов управления (например, кнопки, на которые он хочет нажать :)). Это потому, что дочерние элементы управления не отправляют WM_MOUSEMOVE в родительский диалог.

Единственная функция, которую я обнаружил для «распространения» сообщений WM_MOUSEMOVE от дочерних элементов управления, - это SetCapture (). И он выполняет свою работу, но 1) пользователь не может нажать ни одну кнопку после этого и 2) значок мыши меняется на песочные часы. Так что это не вариант.

Какие-либо предложения?

Обновление. Я поместил вызов TrackMouseEvent в подпрограмму PreTranslateMessage, которая правильно вызывается при любых событиях перемещения мыши (даже при наведении курсора на дочерние элементы управления). Странно то, что WM_MOUSELEAVE все еще генерируется, когда пользователь наводит курсор на дочерний элемент управления! Похоже, TrackMouseEvent знает, какой элемент теперь завис. Любые идеи, как это исправить?


Ответы:


1

Я бы попробовал CDialog::PreTranslateMessage(), если это модальный диалог. Если вы по-прежнему не можете обнаружить движения мыши внутри дочерних элементов, остается единственный вариант - SetWindowsHookEx + WH_MOUSE.

02.02.2012
  • Хорошая идея, но у меня все еще есть проблемы. См. Раздел обновлений в моем сообщении. 02.02.2012
  • Я имел в виду; вообще не используйте TrackMouseEvent. Вместо этого попробуйте получить WM_MOUSEMOVE в PreTranslateMessage. Если сообщения туда не приходят, то нужно использовать ловушку. Раньше я использовал TrackMouseEvent. Это действительно работает для некоторых сценариев, но часто этого недостаточно. 03.02.2012

  • 2

    Когда 2 диалога получают событие, тогда принудительно вызывает событие WM_MOUSELEAVE дочернего диалога. см. код ниже

        void CDlgParent::OnMouseMove(UINT nFlags, CPoint point)
        {
            CWnd* cwnd = this->GetDlgItem(IDC_CHILDRENNAME);
            ::SendMessage(cwnd->m_hWnd, WM_MouseLeave());
    
            CDialog::OnMouseMove(nFlags, point);
        }
    
        void CDlgMain::OnMouseMove(UINT nFlags, CPoint point)
        {
            TRACKMOUSEEVENT tme;
    
            tme.cbSize = sizeof(tme);
            tme.hwndTrack = m_hWnd;
            tme.dwFlags = TME_LEAVE;
            tme.dwHoverTime = HOVER_DEFAULT;
            TrackMouseEvent(&tme);
            ::SetFocus(this->mhWnd);
    
            CDialog::OnMouseMove(nFlags, point);
        }
    

    Как ты думаешь?

    03.02.2012
  • Это то, что я бы назвал экстренным взломом. Как правило, отправлять такие внутренние сообщения Windows - плохая идея. Однако это должно сработать. 03.02.2012
  • Проблема в том, что мне нужно добавить такие обработчики для ВСЕХ дочерних элементов управления. Т.е. если я добавлю новую кнопку, я должен обработать на ней MouseMove. Это можно сделать, но в крайнем случае :) 03.02.2012
  • @NOPslider ок. Я понял. Итак, у меня есть вопрос. как насчет создания собственного сообщения, чем sendmessage? 03.02.2012

  • 3

    Думаю, теперь я понимаю проблему. Это действительно немного сложно. Я думаю, вам нужен таймер, чтобы гарантировать, что последующее сообщение WM_MOUSEMOVE будет обработано (вы должны это проверить).

    BOOL CTestDgDlg::PreTranslateMessage(MSG* pMsg)
    {
        if (pMsg->message == WM_MOUSEMOVE)
        {
            TCHAR buffer[255];
            ::GetWindowText(pMsg->hwnd, buffer, 255);
            TRACE(_T("WM_MOUSEMOVE: %s\n"), buffer);
        }
    
        return CDialogEx::PreTranslateInput(pMsg);
    }
    

    Обращайтесь с WM_MOUSELEAVE, ждите WM_MOUSEMOVE. Это пришло? Нет -> закрыть диалоговое окно. Да -> перезапустить.

    03.02.2012
  • Что, если пользователь перемещает курсор на кнопку (создается WM_MOUSELEAVE), а затем удерживает его (WM_MOUSEMOVE не создается)? Похоже, что в этом случае диалог будет закрыт. 03.02.2012

  • 4

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

    void CALLBACK EXPORT TimerProc(HWND hWnd, UINT nMsg, UINT nIDEvent, DWORD dwTime)
    {
      // This is a little hack, but suggested solution with TrackMouseEvent is quite 
      // unreliable to generate WM_MOUSELEAVE events
      POINT pt;
      RECT rect;
      GetCursorPos(&pt);
      GetWindowRect(hWnd, &rect);
    
      HWND hFGW = GetForegroundWindow();
    
      // Send leave message if cursor moves out of window rect or window 
      // stops being foreground
      if (!PtInRect(&rect, pt) || hFGW != hWnd)
      {
        PostMessage(hWnd, WM_MOUSELEAVE, 0, 0);
      }
    }
    
    03.02.2012
  • Хорошо. Тем не менее, я бы старался избегать отправки таких системных сообщений, которые не подлежат отправке. Если вы используете таймер, почему бы просто не поместить его в сам класс диалога в форме GetCursorPos, GetWindowRect, PtInRect, OnCancel()? 03.02.2012
  • Что вы называете сообщениями, не подлежащими отправке? Я не увидел разницы и просто скопировал этот код обработчика таймера из MSDN. 03.02.2012
  • Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..