Domanda

It seems that CListCtrl doesn't send mouse up events unless there is a double click.

I've attempted sending the missing message from the mouse down handlers to compensate but that causes other undesirable behavior. Then I thought I could send the message within the mouse move handler to be a bit more accurate by checking state. However these are horrible hacks and apart from being ugly they may not work properly for every possible implementation of derived controls.

If anyone knows why mouse up events are not received I'd be curious. More importantly how do I get the CListCtrl using the LVS_OWNERDATA style to send mouse up messages like every other control?

EDIT: I am aware of LVN_BEGINDRAG, LVN_BEGINRDRAG, etc however in order to use these I would need to prevent WM_LBUTTONDOWN, WM_RBUTTONDOWN, and WM_MOUSEMOVE from going to the parent window or DragDropManager hooked into CWinAppEx/CMDIFrameWndEx so I could make a special one-off case for this control to work with the existing system.

This is because I have a central drag and drop manager which can notify various types of controls when to begin a drag operation, when to end, cancel, change animation, pass display objects for source and target in custom messages, etc. It needs to be flexible enough to have differing way of initiating as well as differing actions depending on the control, the input, the type of items selected or targeted, differing control types including 3D, or even different applications, etc.

È stato utile?

Soluzione

For reference here's what I have that works but it's a shameful hack. If no one can come up with anything better than this that's really sad.

Header:

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

Implementation:

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

Altri suggerimenti

You don't need those events. The control provides you with everything you need.

From your code I gather that you want to implement drag'n'drop of items in the list control. Instead of hacking together something that won't work anyway and badmouth MS, do it the right way instead:

Handle the LVN_BEGINDRAG notification to start the drag operation.

Can you please go through the following link and let us know if it worked .

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

You can get an idea from there.

You may be best served by Stefan's answer....

However, you could hook the CListCtrl's winproc (hell, in MFC you could just subclass the CListCtrl, provide your own virtual WindowProc() and forward / intercept whatever mouse messages you want.

Just use the standard MFC subclass a control mechanic at runtime to "substitute" your class for the standard CListCtrl on whatever dialog or window you wish.

I assume you know how to do that?

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top