Получение WM_MOUSEMOVE от дочерних элементов управления на MFC CDialog - PullRequest
2 голосов
/ 02 февраля 2012

У меня есть диалог, полученный из 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 знает, какой элемент управления теперь находится. Есть идеи как это исправить?

Ответы [ 4 ]

1 голос
/ 02 февраля 2012

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

0 голосов
/ 03 февраля 2012

Спасибо за вашу помощь, ребята. Я не смог сделать 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);
  }
}
0 голосов
/ 03 февраля 2012

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

0 голосов
/ 03 февраля 2012

Когда 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);
    }

Как вы думаете?

...