2

I'm trying to customize a treeview control using NM_CUSTOMDRAW messages. I simply want to draw every other visible item with a grey color. Here is code to draw:

INT CResourceOutliner::On_WM_NOTIFY( HWND hDlg, WPARAM wParam, LPARAM lParam )
{
    HWND hTree = GetDlgItem( hDlg, IDC_TREE1 ); 
    switch( ( ( LPNMHDR )lParam )->code )
    {
    ...
    case NM_CUSTOMDRAW:
            {           
                LPNMTVCUSTOMDRAW pCustomDraw = ( LPNMTVCUSTOMDRAW )lParam;
                switch( pCustomDraw->nmcd.dwDrawStage )
                {               
                case CDDS_PREPAINT:
                    return CDRF_NOTIFYITEMDRAW;
                case CDDS_ITEMPREPAINT:
                    {
                        switch ( pCustomDraw->iLevel )                  
                        {
                            // painting all 0-level items blue,                     
                            // and all 1-level items red (GGH)25.                       
                        case 0:
                            {
                            if( pCustomDraw->nmcd.uItemState == ( CDIS_FOCUS | CDIS_SELECTED ) )                            
                                pCustomDraw->clrTextBk = RGB( 255, 255, 255 );                          
                            else                                    
                                pCustomDraw->clrTextBk = RGB( 128, 128, 128 );                          
                                break;
                            }
                        case 1:                         
                            {
                                if( pCustomDraw->nmcd.uItemState == ( CDIS_FOCUS | CDIS_SELECTED ) )                            
                                    pCustomDraw->clrTextBk = RGB( 255, 255, 255 );                          
                                else    
                                    pCustomDraw->clrTextBk = RGB( 128, 128, 128 );
                                break;
                            }
                        default:
                            break;
                        }
                        return CDRF_SKIPDEFAULT;
                    }
                default:
                    break;
                }
            }
    ...
    }
}

This code is from here .

The problem is that after returning CDRF_NOTIFYITEMDRAW on CDDS_PREPAINT notification message, the CDDS_ITEMPREPAINT message never comes...Is there an option to set in order to enable custom drawing..? I imagine that there isn't because the CDDS_PREPAINT message is sent by the control...

...also the code above is not meant to draw every other item...its just a demo from codeguru.com

here is message handling implementation...

int CResourceOutliner::DoModal( int resID, RECT rct, HWND hParent )
{   
    // Set properties
    m_dwpSaveThis = ( DWORD_PTR )this; /// store this pointer
    m_nResId = resID;
    m_hParent = hParent;

    m_hWindow = CreateDialog( GetModuleHandle( NULL ), MAKEINTRESOURCE( m_nResId ), m_hParent, ( DLGPROC )MsgProcStatic );

    // Set window position
    SetWindowPos( m_hWindow, 0, rct.left, rct.top, rct.right, rct.bottom, 0 );

    ShowWindow( m_hWindow, SW_HIDE );

    if( m_hWindow )
        return 1;

    return 0; 
}
INT CALLBACK CResourceOutliner::MsgProcStatic( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
    if( !m_hWindow )
        m_hWindow = hWnd;

    CResourceOutliner *pDlg = ( CResourceOutliner* )m_dwpSaveThis;
    if( pDlg )
        return pDlg->MsgProc( hWnd, uMsg, wParam, lParam );
    else
        return 0;
}
INT CALLBACK CResourceOutliner::MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
    switch( uMsg )
    {
    case WM_INITDIALOG:
        On_WM_INITDIALOG( hWnd, wParam, lParam );
        break;
    case WM_COMMAND:
        On_WM_COMMAND( hWnd, wParam, lParam );
        break;
    case WM_NOTIFY:
        {           
            return On_WM_NOTIFY( hWnd, wParam, lParam );
        }
    case WM_LBUTTONDOWN:
        On_WM_LBUTTONDOWN( hWnd, wParam, lParam );
        break;
    case WM_LBUTTONUP:
        On_WM_LBUTTONUP( hWnd, wParam, lParam );
        break;
    case WM_MOUSEMOVE:
        On_WM_MOUSEMOVE( hWnd, wParam, lParam );
        break;
    case WM_PAINT:
        On_WM_PAINT( hWnd, wParam, lParam );
        break;
    case WM_CLOSE:
        On_WM_CLOSE( hWnd, wParam, lParam );
        break;
    default:
        return 0;
    }
    return 0;
}
P. Avery
  • 779
  • 1
  • 16
  • 34
  • 1
    I didn't touch custom draw code like this in a long time, however I checked one of my old working projects and it appears that code returns `CDRF_NOTIFYITEMDRAW` on `dwDrawStage & CDDS_ITEM` instead of `dwDrawStage & CDDS_PREPAINT`. – nmaier Sep 14 '13 at 20:22
  • @nmaier I tried your suggestion but the function does not receive a CDDS_ITEM notification message...the only value of drawstage received by the function is "1" ( CDDS_PREPAINT )... – P. Avery Sep 14 '13 at 20:56
  • @nmaier maybe it has to do with my handling of messages...check out edited code above...sorry for formatting... – P. Avery Sep 14 '13 at 21:07
  • Eek. I misread my own code. I actually have essentially the sledgehammer solution: `if (!(cd->nmcd.dwDrawStage & CDDS_ITEM)) { return CDRF_NOTIFYITEMDRAW; }` – nmaier Sep 14 '13 at 21:18
  • @nmaier happens...should I be returning CDDS_NOTIFYITEMDRAW or should I be sending a notification message myself? – P. Avery Sep 14 '13 at 21:32
  • As I said, my memory is wonky... But my code, that apparently still works, just returns. – nmaier Sep 14 '13 at 21:36

1 Answers1

5

Most return codes from the dialog procedure need to be set via DWLP_MSGRESULT, for example:

SetWindowLongPtr(hWnd, DWLP_MSGRESULT, CDRF_NOTIFYITEMDRAW);

There are very few exceptions to this rule (WM_CTLCOLORSTATIC is one example that returns directly) as a dialog procedure is generally defined as returning TRUE or FALSE depending on whether the message was handled or not.

Jonathan Potter
  • 36,172
  • 4
  • 64
  • 79