2

I'm experimenting with using NM_CUSTOMDRAW instead of WM_DRAWITEM for bitmap buttons in my win32 app. The WM_DRAWITEM stuff works fine - except that it doesn't work under WINE with a desktop theme enabled (for some reason, with a theme enabled, WINE only sends WM_DRAWITEM when you click a pushbutton).

Anyway, I tried removing the BS_OWNERDRAW from the OK button below - leaving the others alone. To test processing the WM_NOTIFY, I just copy the fields I need from the NMCUSTOMDRAW struct to a DRAWITEMSTRUCT and pass that to my existing WM_DRAWITEM handler. The button draws fine, but then Windows draws the OK text over mine (my text is shifted to make room for the checkmark). I've pasted the code below. I thought that if I returned CDRF_SKIPDEFAULT in response to all NM_CUSTOMDRAW notifications, Windows wouldn't try to draw anything. There's obviously something else I need to do...

enter image description here

    case WM_NOTIFY:
    // Only intrested in NM_CUSTOMDRAW messages here.
    nmh = (LPNMHDR) lParam;
    if (nmh->code != NM_CUSTOMDRAW)
        break;
    // Only interested in CDDS_PREPAINT.
    lpNMC = (LPNMCUSTOMDRAW) lParam;
    if (lpNMC->dwDrawStage != CDDS_PREPAINT)
        return(CDRF_SKIPDEFAULT);

    // Copy fields we need from NMCUSTOMDRAW to a DRAWITEMSTRUCT.
    memset(&dis, 0, sizeof(dis));
    dis.hwndItem = nmh->hwndFrom;
    dis.hDC = lpNMC->hdc;
    dis.rcItem = lpNMC->rc;
    if (lpNMC->uItemState & CDIS_FOCUS)
        dis.itemState |= ODS_FOCUS;
    if (lpNMC->uItemState & CDIS_SELECTED)
        dis.itemState |= ODS_SELECTED;
    if (lpNMC->uItemState & CDIS_DEFAULT)
        dis.itemState |= ODS_DEFAULT;
    if (lpNMC->uItemState & CDIS_DISABLED)
        dis.itemState |= ODS_DISABLED;
    DrawBitmapButtonOnWindowsDialog(wParam, (LPARAM) &dis, -1);
    return(CDRF_SKIPDEFAULT);

case WM_DRAWITEM:
    DrawBitmapButtonOnWindowsDialog(wParam, lParam, -1);
    break;
littlenoodles
  • 435
  • 4
  • 13
  • What happens if you don't `return(CDRF_SKIPDEFAULT);` when `lpNMC->dwDrawStage != CDDS_PREPAINT`, but rather let it just fall through to `DefWindowProc()`? – andlabs Oct 26 '16 at 17:15
  • Same thing - except that now Windows underlines the O in OK - and draws a focus rectangle when the button has focus - and fades it out when the button loses focus. – littlenoodles Oct 26 '16 at 17:41
  • Are you able to reproduce this on a non-WINE Windows system? – Remy Lebeau Oct 26 '16 at 18:02
  • 1
    This is happening on Windows 7. If I run the same code on WINE with a theme enabled, it themes the OK button (i.e. doesn't call my WM_NOTIFY handler at all), and doesn't paint the Cancel and Help buttons at all (i.e., no WM_DRAWITEM messages sent). On WINE without a desktop them enabled, it draws the OK button completely (doesn't call my WM_NOTIFY stuff), and the Cancel and Help buttons get ownerdrawn by me normally via WM_DRAWITEM. So, apparently handling NM_CUSTOMDRAW isn't going to fix my WINE themeing problem - but I'd still like to understand how to do it... – littlenoodles Oct 26 '16 at 19:07
  • If you have a dialog you need to return `CDRF_SKIPDEFAULT` via `SetWindowLongPtr(hWnd, DWLP_MSGRESULT, ...)`. As it is, Windows thinks you're not doing any drawing at all. – Jonathan Potter Oct 26 '16 at 19:26
  • Can you point me to instruction on how and where to call this. I tried SetWindowLongPtr(nmh->hwndFrom, DWLP_MSGRESULT, CDRF_SKIPDEFAULT); return(CDRF_SKIPDEFAULT); And it didn't help. – littlenoodles Oct 26 '16 at 19:33
  • When I call SetWindowLongPtr with the hDlg, the OK button is simply not drawn. When I call it with the hwndFrom (button window handle), the button is drawn as before (with the OK text doubled), but then the app crashes when I hit the Cancel button. Missing something? – littlenoodles Oct 26 '16 at 19:43
  • 1
    The return value should be set on the dialog (the window the notification message was send to). If you return `CDRF_SKIPDEFAULT` from `CDDS_PREPAINT` you are telling Windows you have done all the painting yourself. If you return `CDRF_DODEFAULT` (0, the default value in other words) you are telling Windows you haven't done any painting yourself. Exactly what painting is it you want to do? – Jonathan Potter Oct 27 '16 at 01:49
  • My buttons are custom drawn (they essentially look like Windows 7 buttons, but...). Probably don't need to be, but the code was written before Windows could put bitmaps on buttons - and in any case, it's a C app, not C++ - so I'm not even sure I can use the button classes that support bitmaps. Plus I support multiple colors. Anyway, this all works fine using BS_OWNERDRAW, but that only works under WINE if I don't select a desktop theme. I was just hoping to find a way to get WINE to handle owner drawn buttons with a theme. – littlenoodles Oct 27 '16 at 13:49
  • For what it's worth, the whole CDRF_SKIPDEFAULT thing *is* working - for the graphical part of the button drawing. Windows isn't attempting to draw the button at all, and my custom draw code is working normally. The problem is that Windows draws the text over my button. I'm wondering if there's some other intercept I need to do to let Windows know that I already drew the text as well as the button... – littlenoodles Oct 27 '16 at 15:01
  • Are you loading version 6 of comctl32 (e.g. via manifest)? Because buttons don't support custom draw otherwise. "Custom draw is also supported for button controls if you have an application manifest to ensure that Comctl32.dll version 6 is available." https://msdn.microsoft.com/en-us/library/windows/desktop/bb775528(v=vs.85).aspx – Fizz Oct 28 '17 at 03:23

0 Answers0