2011-10-12 77 views
1

似乎CListCtrl不發送鼠標事件,除非有雙擊。MFC CListCtrl吃鼠標事件?

我試圖從鼠標向下處理程序發送丟失的消息來彌補,但這會導致其他不良行爲。然後,我想我可以通過檢查狀態來發送鼠標移動處理程序中的消息以更準確一些。然而,這些都是可怕的黑客攻擊,除了醜陋之外,它們可能無法正確執行派生控制的每個可能的實現。

如果有人知道爲什麼沒有收到鼠標事件,我會很好奇。更重要的是如何使用LVS_OWNERDATA樣式獲得CListCtrl樣式,以便像其他控件一樣發送鼠標信息?

編輯:我知道LVN_BEGINDRAGLVN_BEGINRDRAG等但爲了使用這些,我需要防止WM_LBUTTONDOWNWM_RBUTTONDOWNWM_MOUSEMOVE從去勾搭成CWinAppEx/CMDIFrameWndEx父窗口或DragDropManager,所以我可以做這個控制與現有系統一起工作的特殊情況。

這是因爲我有一箇中央拖放管理器,可以通知各種類型的控件何時開始拖動操作,何時結束,取消,更改動畫,傳遞自定義消息中源和目標的顯示對象等它需要具有足夠的靈活性,以根據控制,輸入,所選項目或目標的類型,不同的控制類型(包括3D,甚至不同的應用程序等)具有不同的啓動方式以及不同的操作。

+0

-1如果您有與社區共享的代碼與您的問題直接相關,則需要在問題的主體中發生 - 並非答案。這顯然不是一個有效的答案,或者候選人是一個答案。它讓你更難理解你的問題,因爲通常不會爲問題的額外部分搜索答案。 – Mordachai

+0

我把它作爲答案,因爲沒有人有任何適用的答案,更好,顯然這是我將與之合作的解決方案。 – AJG85

回答

2

作爲參考,這裏是我有這個作品,但它是一個可恥的黑客。如果沒有人能拿出比這更令人難過的東西。

頁眉:

#pragma once 

// CListCtrlEx 
class CListCtrlEx : public CListCtrl 
{ 
    DECLARE_DYNAMIC(CListCtrlEx) 

public: 
    CListCtrlEx(); 
    virtual ~CListCtrlEx(); 

    bool IsSelected(int index); 
    BOOL SelectDropTarget(int item); 

protected: 
    DECLARE_MESSAGE_MAP() 

    afx_msg void OnStateChanged(NMHDR* pNMHDR, LRESULT* pResult); 

    afx_msg void OnLButtonDown(UINT nFlags, CPoint point); 
    afx_msg void OnRButtonDown(UINT nFlags, CPoint point); 
    afx_msg void OnLButtonUp(UINT nFlags, CPoint point); 
    afx_msg void OnRButtonUp(UINT nFlags, CPoint point); 
    afx_msg void OnMouseMove(UINT nFlags, CPoint point); 

private: 
    bool m_lbDown; 
    bool m_rbDown; 
}; 

實現:

#include "stdafx.h" 
#include "ListCtrlEx.h" 

// CListCtrlEx 
IMPLEMENT_DYNAMIC(CListCtrlEx, CListCtrl) 

CListCtrlEx::CListCtrlEx() : m_lbDown(false), m_rbDown(false) 
{ 
} 

CListCtrlEx::~CListCtrlEx() 
{ 
} 

BEGIN_MESSAGE_MAP(CListCtrlEx, CListCtrl) 
    ON_WM_LBUTTONDOWN() 
    ON_WM_RBUTTONDOWN() 
    ON_WM_LBUTTONUP() 
    ON_WM_RBUTTONUP() 
    ON_WM_MOUSEMOVE() 

    ON_NOTIFY_REFLECT(LVN_ODSTATECHANGED, &CListCtrlEx::OnStateChanged) 
END_MESSAGE_MAP() 

// CListCtrlEx message handlers 
void CListCtrlEx::OnLButtonDown(UINT nFlags, CPoint point) 
{ 
    m_lbDown = true; 
    CListCtrl::OnLButtonDown(nFlags, point); 
} 

void CListCtrlEx::OnRButtonDown(UINT nFlags, CPoint point) 
{ 
    m_rbDown = true; 
    CListCtrl::OnRButtonDown(nFlags, point); 
} 

void CListCtrlEx::OnLButtonUp(UINT nFlags, CPoint point) 
{ 
    m_lbDown = false; 
    CListCtrl::OnLButtonUp(nFlags, point); 
} 

void CListCtrlEx::OnRButtonUp(UINT nFlags, CPoint point) 
{ 
    m_rbDown = false; 
    CListCtrl::OnRButtonUp(nFlags, point); 
} 

void CListCtrlEx::OnMouseMove(UINT nFlags, CPoint point) 
{ 
    if (m_lbDown && ((nFlags & MK_LBUTTON) == 0)) 
    { 
     PostMessage(WM_LBUTTONUP, 
      MAKEWPARAM(LOWORD(nFlags), HIWORD(nFlags)), 
      MAKELPARAM(point.x, point.y)); 
    } 

    if (m_rbDown && ((nFlags & MK_RBUTTON) == 0)) 
    { 
     PostMessage(WM_RBUTTONUP, 
      MAKEWPARAM(LOWORD(nFlags), HIWORD(nFlags)), 
      MAKELPARAM(point.x, point.y)); 
    } 

    CListCtrl::OnMouseMove(nFlags, point); 
} 

bool CListCtrlEx::IsSelected(int index) 
{ 
    return (GetItemState(index, LVIS_SELECTED) & LVIS_SELECTED) != 0; 
} 

// highlight drop targets sort of like CTreeCtrl 
BOOL CListCtrlEx::SelectDropTarget(int item) 
{ 
    static int prevHighlight(-1); 
    if (item >= 0 && item < GetItemCount()) 
    { 
     if (item != prevHighlight) 
     { 
      if (prevHighlight >= 0) 
      { 
       SetItemState(prevHighlight, 0, LVIS_DROPHILITED); // remove highlight from previous target 
       RedrawItems(prevHighlight, prevHighlight); 
      } 

      prevHighlight = item; 
      SetItemState(item, LVIS_DROPHILITED, LVIS_DROPHILITED); // highlight target 
      RedrawItems(item, item); 

      UpdateWindow(); 
      return TRUE; 
     } 
    } 
    else 
    { 
     for (int i(0); i < GetItemCount(); ++i) 
      SetItemState(i, 0, LVIS_DROPHILITED); // un-highlight all 
     prevHighlight = -1; 
    } 

    return FALSE; 
} 

void CListCtrlEx::OnStateChanged(NMHDR* pNMHDR, LRESULT* pResult) 
{ 
// MSDN: 
// If a list-view control has the LVS_OWNERDATA style, 
// and the user selects a range of items by holding down the SHIFT key and clicking the mouse, 
// LVN_ITEMCHANGED notification codes are not sent for each selected or deselected item. 
// Instead, you will receive a single LVN_ODSTATECHANGED notification code, 
// indicating that a range of items has changed state. 

    NMLVODSTATECHANGE* pStateChanged = (NMLVODSTATECHANGE*)pNMHDR; 

    // redraw newly selected items 
    if (pStateChanged->uNewState == LVIS_SELECTED) 
     RedrawItems(pStateChanged->iFrom, pStateChanged->iTo); 
} 
+0

好的,你是CListCtrl的子類 - 那是什麼問題呢? – Mordachai

+0

由於默認情況下不會發送虛假鼠標消息。 – AJG85

0

你不需要這些事件。該控件爲您提供所需的一切。

從你的代碼中我收集到你想要在列表控件中實現drag'n'drop。 而不是黑客一起反正無法正常工作和惡臭MS,反而做正確的方式:

處理LVN_BEGINDRAG通知開始拖動操作。

+0

這個想法是爲源和目標設置一箇中央拖放管理器和接口,以便它可以使用SM_CXDRAG和SM_CYDRAG的系統指標在3D視圖,樹,編輯,列表等中工作,而無需重複編寫相同的邏輯。 CListCtrl是唯一存在問題的地方。樹的 – AJG85

+0

控制其TVN_BEGINDRAG而不是LVN_BEGINDRAG。其他控件提供類似的通知。 – Stefan

+0

我想我可以製作一些自定義消息,並讓控件在他們想要拖動的時候通知管理員,而不是通知管理員通知控件。我認爲問題仍然存在,即開始拖拽需要在一個控件上,而拖拽可能在另一個控件上。 – AJG85

-1

您可以通過Stefan的回答可以提供最好的服務....

但是,你可以掛鉤的CListCtrl的Winproc傳(地獄,在MFC你可以繼承CListCtrl的子類,提供你自己的虛擬WindowProc()並轉發/攔截你想要的任何鼠標消息。

只需在運行時使用標準MFC子類控件機制,就可以在任何對話框或窗口中將您的類替換爲標準CListCtrl。

我假設你知道該怎麼做?

+0

你似乎錯過了整個問題,就是鼠標上來的消息不是默認發送或接收的行爲。 Stefan的答案只適用於'CTreeCtrl'和'CListCtrl'我需要自定義控件,3d視圖,其他應用程序等。 – AJG85

+0

您似乎無法理解Win32的工作原理。 「CTreeCtrl」不發送WM_LBUTTONUP消息 - 它從操作系統接收它們。所以如果你直接從操作系統截獲這條消息,那麼你不會錯過它。它不能被隱藏。只有當你依賴消息鏈中的某些點時,它才能隱藏起來。 您的回覆和-1對於過去13年來一直在編寫MFC應用程序的人來說非常煩人。 – Mordachai

0

請您通過以下鏈接,讓我們知道它是否工作。

http://support.microsoft.com/kb/147842

你可以從那裏得到一個想法。

+0

在鼠標上不起作用的'NM_CLICK' /'NM_RCLICK'不會啓動鼠標。這對於命中測試是有利的,並且可以檢查或修改選擇,因爲它不是鼠標事件的通知,但它與其他方面無關。之前我在Google上搜索過類似的文章,並試圖無效。 – AJG85

+0

問題是您無法收到鼠標事件。讓我試試這個。 –

+0

是的,我做了一個單獨的測試應用程序,只是有一個'CListCtrl'對話框,並發現相同的行爲。這就是我現在使用的合成鼠標信息的方法。 – AJG85