C++ 从MFC CDialog上的子控件接收WM_MOUSEMOVE

C++ 从MFC CDialog上的子控件接收WM_MOUSEMOVE,c++,visual-studio-2010,mfc,mouseevent,C++,Visual Studio 2010,Mfc,Mouseevent,我的对话框来自CDialog,一旦用户将鼠标光标移离它,我就想关闭它。为此,我添加了调用OnCancel()的OnMouseLeave处理程序。据我所知,为了及时发送WM_MOUSELEAVE事件,必须在OnMouseMove例程中调用TrackMouseeEvent。因此,整个代码如下所示: void CDlgMain::OnMouseLeave() { CDialog::OnMouseLeave(); // Close dialog when cursor is going out o

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

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)鼠标图标变为沙漏。所以这不是一个选择

有什么建议吗


Update我将TrackMouseEvent调用放在PreTranslateMessage例程中,该例程在任何鼠标移动事件(甚至悬停子控件)上都能正确调用。奇怪的是,当用户将鼠标悬停在子控件上时,仍然会生成WM_MOUSELEAVE!似乎TrackMouseeEvent知道现在悬停的控件是什么。有没有办法解决这个问题?

如果这是一个模式对话框,我会尝试使用
CDialog::PreTranslateMessage()
。如果您仍然无法检测到子对话框中的鼠标移动,则剩下的唯一选项是当2 dialog获取事件时,将子对话框的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);
    }

你觉得怎么样?

我想我现在明白这个问题了。这确实有点棘手。我认为您需要一个计时器来保证后续的
WM_MOUSEMOVE
消息得到处理(您必须对此进行测试)


处理
WM\u mouseleve
,等待
WM\u mouseleve
。它到了吗?否->取消对话框。是->重新启动。

谢谢你们的帮助,伙计们。我无法正确地制作TrackMouseeEvent,所以我最终实现了带有计时器的解决方案。在每次勾选时,我都会检查鼠标光标在对话框区域内的位置,并确保它仍然是前景。这对我来说是完美的,尽管它有点粗糙

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

好主意,但我还是有问题。请参阅我帖子中的更新部分。我的意思是;根本不要使用TrackMouseEvent。相反,请尝试在预翻译消息中获取WM_MOUSEMOVE。如果消息没有到达那里,那么您需要使用钩子。我以前用过TrackMouseEvent。它确实适用于某些场景,但通常是不够的。我称之为“紧急黑客”。一般来说,发送这样的Windows内部消息是个坏主意。但是,它应该可以工作。问题是我需要为所有子控件添加这样的处理程序。也就是说,如果我添加了一个新按钮,我应该在上面处理MouseMove。这是可以做到的,但作为最后手段:)@nopslook。我得到了它。所以,我有一个问题。如果用户将光标移动到按钮(生成WM_MOUSELEAVE),然后将其保持静止(不生成WM_MOUSEMOVE),该怎么办?看来在这种情况下dialog将被取消。很好。不过,我会尽量避免发送这样的不可发送的系统消息。如果您使用计时器,为什么不简单地将其以
GetCursorPos
GetWindowRect
PtInRect
OnCancel()
的形式放入对话框类本身?您称之为“不可发送”消息是什么?我看不出有什么不同,只是从MSDN复制粘贴了这个计时器处理程序代码。
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);
  }
}