I have a dialog window (with CDialogEx
based class) containing a single list control (report view, with CListCtrl
attached as a control variable). I want to check which line of the displayed list was double clicked.
I thought it will be just a matter of adding OnNotify
and converting the double click coordinates to the line number:
BOOL MyDialog::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT *pResult)
{
if (((NMMOUSE *)lParam)->hdr.idFrom == IDC_MYLIST &&
((NMMOUSE *)lParam)->hdr.code == NM_DBLCLK)
{
CPoint cp(((LPNMMOUSE)lParam)->pt);
int iLine = myListCtrl.HitText(cp);
It partially works: I am correctly catching doubleclick notifications coming from the control. What doesn't work though is I don't get correct double click coordinates. cp contains some numbers as coordinates, but they are wrong. x is always 0, and y is what x should be.
Any ideas where I am going wrong and what should I do? With some digging it looks like lParam
points to some structure containing the coordinates, but it is not NMMOUSE
as I expected, and I can't find any information about what other structure it could be.
This is deep into undefined behavior from the get-go: The only guarantee you get from the system when receiving a WM_NOTIFY
message is that lParam
points to an NMHDR
structure. That's a piece of generic information provided to direct subsequent processing.
Consequently, the only safe unconditional cast in an OnNotify
override is from LPARAM
into NMHDR const*
, so let's just do that to keep our sanity:
BOOL MyDialog::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT *pResult)
{
NMHDR const& hdr = *reinterpret_cast<NMHDR const*>(lParam);
if (hdr.idFrom != IDC_MYLIST || hdr.code != NM_DBLCLK)
return false;
// ...
Once the preconditions have been established, it's safe to move on. At this point we know that the origin passed our filter (IDC_MYLIST
), and that the notification is an NM_DBLCLK
, which passes additional information by way of a pointer to an NMITEMACTIVATE
structure.
The following takes advantage of that additional knowledge, and produces a CPoint
holding the double-click position:
// ...
NMITEMACTIVATE const& info = *reinterpret_cast<NMITEMACTIVATE const*>(lParam);
CPoint const cp = CPoint(info.ptAction);
// ...