从MFC CDialog上的子控件接收WM_MOUSEMOVE

时间:2012-02-02 15:53:38

标签: c++ visual-studio-2010 mfc mouseevent

我的对话框来自CDialog,我希望一旦用户将鼠标光标移离它就关闭它。为此,我添加了OnMouseLeave处理程序,它调用OnCancel()。据我了解,要及时发送WM_MOUSELEAVE事件,必须在OnMouseMove例程内调用TrackMouseEvent。所以整个代码如下:

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 个答案:

答案 0 :(得分:1)

如果这是一个模态对话框,我会尝试CDialog::PreTranslateMessage()。如果仍无法检测到孩子内部的鼠标移动,则唯一的选项是SetWindowsHookEx + WH_MOUSE

答案 1 :(得分:0)

当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);
    }

你觉得怎么样?

答案 2 :(得分:0)

我想我现在明白了这个问题。这确实有点棘手。我认为您需要一个计时器来保证后续的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。它到了吗?不 - >解雇对话。是的 - >重新启动。

答案 3 :(得分:0)

感谢您的帮助,伙计们。我无法正确制作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);
  }
}