2012-02-02 100 views
2

我有我的對話框派生自CDialog,我想在用戶將鼠標光標從它移開後關閉它。爲此,我添加了OnMouseLeave處理程序,它調用OnCancel()。據我所知,爲了及時發送WM_MOUSELEAVE事件,必須在OnMouseMove例程中調用TrackMouseEvent。所以整個代碼如下:從CDialog上的子控件接收WM_MOUSEMOVE

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知道現在控制什麼。任何想法如何解決這一問題?

回答

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

好。儘管如此,我會盡力避免發送不可發送的系統消息。如果你使用一個定時器,爲什麼不直接把它放在對話框類本身中,以'GetCursorPos','GetWindowRect','PtInRect','OnCancel()'的形式? – l33t 2012-02-03 12:21:20

+1

你稱之爲「不可發送」的消息是什麼?我看到沒有區別,只是從MSDN複製粘貼此計時器處理程序代碼。 – Mikhail 2012-02-03 13:44:40

1

如果這是一個模態對話框,我會嘗試CDialog::PreTranslateMessage()。如果您仍然無法檢測兒童內部的鼠標移動,則剩下的唯一選項是SetWindowsHookEx + WH_MOUSE

+0

好主意,但我仍然有問題。請參閱我的文章中的更新部分。 – Mikhail 2012-02-02 17:18:34

+0

我的意思是;根本不要使用TrackMouseEvent。相反,嘗試在PreTranslateMessage中獲取WM_MOUSEMOVE。如果消息沒有到達那裏,那麼你需要使用一個鉤子。我過去曾使用TrackMouseEvent。它確實適用於某些場景,但通常不夠。 – l33t 2012-02-02 22:20:13

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

您認爲如何?

+1

這就是我所說的「緊急黑客」。通常,像這樣發送內部Windows消息是一個壞主意。但是,它應該工作。 – l33t 2012-02-03 08:07:49

+0

問題是我需要爲所有子控件添加這樣的處理程序。即如果我添加一個新按鈕,我應該在它上面處理MouseMove。它可以完成,但作爲最後的手段:) – Mikhail 2012-02-03 08:32:31

+0

@NOPslider確定。我知道了。所以,我有一個問題。如何使自定義消息比sendmessage? – 2012-02-03 10:46:14

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。它到了嗎?否 - >關閉對話框。是 - >重新啓動。

+1

如果用戶將光標移動到按鈕(生成WM_MOUSELEAVE)然後保持不變(不生成WM_MOUSEMOVE),該怎麼辦?似乎在這種情況下對話將被解僱。 – Mikhail 2012-02-03 08:34:54