Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

? CustomDraw Message Reflection Problem (Bug?)

120 views
Skip to first unread message

Alec S.

unread,
Nov 22, 2008, 11:37:37 PM11/22/08
to
Hi,

I am having trouble with message reflection. I am trying to set up my list
control to handle CustomDraw on its own, but also let the parent override it.
(That is, the control gets CustomDraw notifications to handle, then reflects
them to the parent to handle after—if it wants.)

I have ON_NOTIFY_REFLECT_EX(NM_CUSTOMDRAW,OnNMCustomDraw) in the control’s
message map. The handler has a simple switch on the DrawStage to ask for subitem
notifications, makes a change to the colors per subitem, then returns a BOOL.

The parent dialog has ON_NOTIFY(NM_CUSTOMDRAW,IDC_LIST,OnNMCustomDrawList) in
its message map, either enabled (active) or disabled (commented out).

If the control returns TRUE, then it gets all expected notifications, and the
parent gets none whether it has the handler enabled or not. Good.

If the control returns FALSE, then the parent gets notifications as expected.
Good. The control itself however only gets messages if the parent has a handler
(even empty); if the parent does not have a handler (in the message map), then
the control does not get CDDS_ITEMPREPAINT (or CDDS_SUBITEM or CDDS_POSTPAINT,
etc.) notifications. Bad!

This seems like a bug. Why would the list control not get (sub)item
notifications (or postpaint, etc.) if the parent does not have a handler?


The last paragraph of http://www.microsoft.com/msj/1197/c1197.aspx explains
exactly what I want:
“So if you're implementing a specialized control such as a tree control that
handles its own drag/drop notification, but you also want to let the parent see
it, just use ON_NOTIFY_REFLECT_EX and return FALSE from your handler.”

That’s what I’m doing, but if the parent does not have a handler, the control’s
handler doesn’t work. Any ideas?

Thanks.

--
Alec S.
news/alec->synetech/cjb/net


Alec S.

unread,
Nov 22, 2008, 11:38:17 PM11/22/08
to

Joseph M. Newcomer

unread,
Nov 23, 2008, 1:44:04 PM11/23/08
to
At this point I would be reduced to single-stepping through the MFC window-handler code
that handles reflected messages, probably with conditional breakpoints set so I didn't go
crazy ignoring messages I didn't want. For example, I'd determine the window handle and
not break unless LPARAM was that value. That's all I can suggest right now, because it
really does sound like a bug.
joe

Joseph M. Newcomer [MVP]
email: newc...@flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm

Alec S.

unread,
Nov 24, 2008, 12:12:16 PM11/24/08
to
Joseph M. Newcomer wrote (in news:p09ji4lic5qo8c140...@4ax.com):

> At this point I would be reduced to single-stepping through the MFC
> window-handler code that handles reflected messages, probably with
> conditional breakpoints set so I didn't go crazy ignoring messages I didn't
> want. For example, I'd determine the window handle and not break unless
> LPARAM was that value. That's all I can suggest right now, because it really
> does sound like a bug.

I don’t know about debugging the MFC internal code, but I just did a test with a
bare project and had the same results: the control does not get subsequent
notifications if it returns FALSE and the parent doesn’t have a handler.

Hopefully someone can figure out what’s wrong and/or a fix and/or a workaround.
(I’m using VS.NET 2003 SP1, and have no idea how to report this as a bug to
someone at Microsoft who would hear it and do something about it.) Here’s the
handlers of the control and dialog, and the debug traces (the last one clearly
shows that the control isn’t getting notifications):

BEGIN_MESSAGE_MAP(CMyLC, CListCtrl)
ON_NOTIFY_REFLECT_EX(NM_CUSTOMDRAW, OnNMCustomdraw)
END_MESSAGE_MAP()

BEGIN_MESSAGE_MAP(CaDlg, CDialog)

// This is either active or commented out:
// ON_NOTIFY(NM_CUSTOMDRAW, IDC_LIST, OnNMCustomdrawList)
END_MESSAGE_MAP()

BOOL CMyLC::OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult) {
NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>(pNMHDR);

CString t=_T("");
t.Format(_T("%s\tdrawstage:%d\titem:%d,%d\n"),
_T("\tCCustList::OnNMCustomDraw"), pLVCD->nmcd.dwDrawStage,
pLVCD->nmcd.dwItemSpec, pLVCD->iSubItem);
TRACE(t);

switch (pLVCD->nmcd.dwDrawStage) {
case CDDS_PREPAINT: *pResult=CDRF_NOTIFYITEMDRAW; break;
case CDDS_ITEM|CDDS_PREPAINT: *pResult=CDRF_NOTIFYSUBITEMDRAW; break;
case CDDS_ITEM|CDDS_PREPAINT|CDDS_SUBITEM:
default: *pResult=CDRF_DODEFAULT; break;
}
return FALSE;
}

void CaDlg::OnNMCustomdrawList(NMHDR *pNMHDR, LRESULT *pResult) {
NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>(pNMHDR);

CString t=_T("");
t.Format(_T("%s\tdrawstage:%d\titem:%d,%d\n"), _T("CaDlg::OnNMCustomDrawList"),
pLVCD->nmcd.dwDrawStage, pLVCD->nmcd.dwItemSpec, pLVCD->iSubItem);
TRACE(t);

switch (pLVCD->nmcd.dwDrawStage) {
case CDDS_PREPAINT: *pResult=CDRF_NOTIFYITEMDRAW; break;
case CDDS_ITEM|CDDS_PREPAINT: *pResult=CDRF_NOTIFYSUBITEMDRAW; break;
case CDDS_ITEM|CDDS_PREPAINT|CDDS_SUBITEM:
default: *pResult=CDRF_DODEFAULT; break;
}
}

Yes, TRUE (GOOD):
CCustList::OnNMCustomDraw drawstage:1 item:2010419606,0
CCustList::OnNMCustomDraw drawstage:65537 item:0,0
CCustList::OnNMCustomDraw drawstage:196609 item:0,0
CCustList::OnNMCustomDraw drawstage:196609 item:0,1
CCustList::OnNMCustomDraw drawstage:65537 item:1,1
CCustList::OnNMCustomDraw drawstage:196609 item:1,0
CCustList::OnNMCustomDraw drawstage:196609 item:1,1

No, TRUE (GOOD):
CCustList::OnNMCustomDraw drawstage:1 item:2010419606,0
CCustList::OnNMCustomDraw drawstage:65537 item:0,0
CCustList::OnNMCustomDraw drawstage:196609 item:0,0
CCustList::OnNMCustomDraw drawstage:196609 item:0,1
CCustList::OnNMCustomDraw drawstage:65537 item:1,1
CCustList::OnNMCustomDraw drawstage:196609 item:1,0
CCustList::OnNMCustomDraw drawstage:196609 item:1,1

Yes, FALSE (GOOD):
CCustList::OnNMCustomDraw drawstage:1 item:2010419606,0
CaDlg::OnNMCustomDrawList drawstage:1 item:2010419606,0
CCustList::OnNMCustomDraw drawstage:65537 item:0,0
CaDlg::OnNMCustomDrawList drawstage:65537 item:0,0
CCustList::OnNMCustomDraw drawstage:196609 item:0,0
CaDlg::OnNMCustomDrawList drawstage:196609 item:0,0
CCustList::OnNMCustomDraw drawstage:196609 item:0,1
CaDlg::OnNMCustomDrawList drawstage:196609 item:0,1
CCustList::OnNMCustomDraw drawstage:65537 item:1,1
CaDlg::OnNMCustomDrawList drawstage:65537 item:1,1
CCustList::OnNMCustomDraw drawstage:196609 item:1,0
CaDlg::OnNMCustomDrawList drawstage:196609 item:1,0
CCustList::OnNMCustomDraw drawstage:196609 item:1,1
CaDlg::OnNMCustomDrawList drawstage:196609 item:1,1

No, FALSE (BAD!):
CCustList::OnNMCustomDraw drawstage:1 item:2010419606,0

--
Alec S.
news/alec->synetech/cjb/net

> > If the control returns TRUE, then it gets all expected notifications, and

.rhavin grobert

unread,
Nov 24, 2008, 3:18:14 PM11/24/08
to

I had similar problems with a listview. after some fruitless efforts i
decides to overload onpaint/onbkerase and generated my own
customdrawing routine. nice side-effect is that you can have
CDDS_PREERASE, too;-) the only drawback is that i didnt know how to
implement CDDF_DODEFAULT, e.g. how "backward-return" something to the
default custom drawing routine.

Alec S.

unread,
Nov 24, 2008, 3:52:56 PM11/24/08
to
Alec S. wrote (in news:%236Zhvnl...@TK2MSFTNGP06.phx.gbl):

> Joseph M. Newcomer wrote (in news:p09ji4lic5qo8c140...@4ax.com):
>

> I don’t know about debugging the MFC internal code, but I just did a test
> with a bare project and had the same results: the control does not get
> subsequent notifications if it returns FALSE and the parent doesn’t have a
> handler.

I just did a test with the header control and I think I am on to something. I
set up a CMyDlg dialog that subclasses a CMyLC list control which in turn
subclasses a CMyHeader header control. I then traced the CustomDraw handlers for
each, varying whether they return TRUE or FALSE. Here’s the results (the order
of the handlers called):

CMyHeader TRUE: HEADER
CMyHeader FALSE, CMyList TRUE: HEADER, LIST (alternates)
CMyHeader FALSE, CMyList FALSE, CMyDlg TRUE/FALSE: DIALOG

If I remove the handler from the dialog (OnNotify since it can’t do a direct
handler for the header) then the app doesn’t get any header notifications at
all, anywhere. It seems that the dialog is somehow eating the notifications
before the controls get them when they return FALSE.

Unfortunately returning FALSE is who message reflection works; returning TRUE
doesn’t reflect.

Alec S.

unread,
Nov 24, 2008, 4:39:25 PM11/24/08
to
Okay, this the last test I can think of. I logged the handlers in the header
(H), list (L), and dialog (D) to see which are being hit. A 1 means that the
specified handler returns TRUE, a 0 means it returns FALSE, and the list shows
the order of the handlers being called:

H:1, L:1 - H, H, H, H…
H:1, L:0 - H, H, H, H…
H:0, L:1 - H, L, H, L…
H:0, L:0 - D, D, D, D…

The way I see it is that it should work like this: Windows (W) sends a message
to the header; the header gets the message and reflects it to the list; the list
gets the message and reflects it to the dialog; the dialog gets the message (ie
W->H->L->D). And the chain can be broken by returning TRUE (FALSE would have
made more sense).

As you can see, it seems to work as expected until the last line. I don’t know
if it’s compiler dependant (VS.NET 2003 SP1), so maybe it works with a newer
one—ie, it’s been fixed. I also don’t know if it works in a normal app rather
than a dialog app (it wouldn’t be the first quirk that dialog apps have—like
menus and accelerator keys).

Maybe someone with a newer (or older) VisualStudio can test if message
reflection works.


--
Alec S.
news/alec->synetech/cjb/net

Alec S. wrote (in news:ustBlanT...@TK2MSFTNGP04.phx.gbl):

0 new messages