Search code examples
cwinapitreeview

WinAPI, TreeView: a gray item by TVIS_CUT


I want to make a item gray. According to docs I set TVIS_CUT in stateMask and mask.
But it doesn't work (the item is black as usual). TVIS_BOLD works perfect.

I use CodeBlocks17 (gcc)/Win7x64. Also I tried VS2005 and another OS (WinXP) with the same result.
What did I miss?

#define _UNICODE
#define UNICODE

#include <windows.h>
#include <commctrl.h>

LRESULT CALLBACK cbMain (HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nCmdShow) {
    MSG msg;
    WNDCLASSEX wc{0};

    wc.hInstance = hInstance;
    wc.lpszClassName = TEXT("MyAppClass");
    wc.lpfnWndProc = cbMain;
    wc.style = CS_DBLCLKS;
    wc.cbSize = sizeof (WNDCLASSEX);
    wc.hIcon = LoadIcon(0, IDC_ICON);
    wc.hCursor = LoadCursor (NULL, IDC_ARROW);
    wc.lpszMenuName = 0;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hbrBackground = (HBRUSH) COLOR_BACKGROUND;

    if (!RegisterClassEx (&wc))
        return EXIT_FAILURE;

    HWND hMainWnd = CreateWindowEx (0, TEXT("MyAppClass"), NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, 500, 600, 160, 300, HWND_DESKTOP, NULL, hInstance, NULL);
    HWND hTreeWnd = CreateWindowEx(0, WC_TREEVIEW, NULL, WS_VISIBLE | WS_CHILD | TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT  | TVS_EDITLABELS, 0, 0, 150, 200, hMainWnd, (HMENU)100, hInstance,  NULL);

    TVITEM tvi{0};
    tvi.mask = TVIF_TEXT | TVIF_STATE;
    tvi.pszText = TEXT("Item");
    tvi.cchTextMax = 40;
    tvi.stateMask = TVIS_CUT;
    tvi.state = TVIS_CUT;

    TVINSERTSTRUCT tvins{0};
    tvins.item = tvi;
    tvins.hInsertAfter = TVI_ROOT;
    tvins.hParent = TVI_ROOT;
    SendMessage(hTreeWnd, TVM_INSERTITEM, 0, (LPARAM)(LPTVINSERTSTRUCT)&tvins);

    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return msg.wParam;
}

LRESULT CALLBACK cbMain (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
    switch (message) {
        case WM_DESTROY:
            PostQuitMessage (0);
            break;

        default:
            return DefWindowProc (hwnd, message, wParam, lParam);
    }

    return 0;
}

Solution

  • I used Custom Draw as Jonathan Potter said.

    Here is one pitfall: while a current item changing, old item has CDIS_FOCUS and CDIS_SELECTED flags in pCustomDraw->nmcd.uItemState member, and new item doesn't have. So pCustomDraw->clrText = (pNMTVCD->nmcd.uItemState & CDIS_FOCUS) == CDIS_FOCUS) ? ... will works a little bit strange. I checked pCustomDraw->clrTextBk instead. But it can be broken when user used a custom theme.

    I use this article and an applied code to build the example.

    #define _UNICODE
    #define UNICODE
    #define _WIN32_WINNT 0x0501
    #define _WIN32_IE 0x0500
    
    #include <windows.h>
    #include <commctrl.h>
    
    #define IDC_TREEVIEW 100
    
    LRESULT CALLBACK cbMain (HWND, UINT, WPARAM, LPARAM);
    
    int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nCmdShow) {
        MSG msg;
        WNDCLASSEX wc{0};
    
        wc.hInstance = hInstance;
        wc.lpszClassName = TEXT("MyAppClass");
        wc.lpfnWndProc = cbMain;
        wc.style = CS_DBLCLKS;
        wc.cbSize = sizeof (WNDCLASSEX);
        wc.hIcon = LoadIcon(0, IDC_ICON);
        wc.hCursor = LoadCursor (NULL, IDC_ARROW);
        wc.lpszMenuName = 0;
        wc.cbClsExtra = 0;
        wc.cbWndExtra = 0;
        wc.hbrBackground = (HBRUSH) COLOR_BACKGROUND;
    
        if (!RegisterClassEx (&wc))
            return EXIT_FAILURE;
    
        HWND hMainWnd = CreateWindowEx (0, TEXT("MyAppClass"), NULL, WS_OVERLAPPED | WS_VISIBLE | WS_SYSMENU, 500, 600, 160, 300, HWND_DESKTOP, NULL, hInstance, NULL);
        HWND hTreeWnd = CreateWindowEx(0, WC_TREEVIEW, NULL, WS_VISIBLE | WS_CHILD | TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT  | TVS_EDITLABELS, 0, 0, 150, 200, hMainWnd, (HMENU)IDC_TREEVIEW, hInstance,  NULL);
    
        TVITEM tvi{0};
        TVINSERTSTRUCT tvins{0};
        tvi.mask = TVIF_TEXT | TVIF_STATE;
        tvi.pszText = TEXT("Item1");
        tvi.cchTextMax = 40;
        tvi.stateMask = 0;
        tvi.state = 0;
    
        tvins.item = tvi;
        tvins.hInsertAfter = TVI_ROOT;
        tvins.hParent = TVI_ROOT;
        SendMessage(hTreeWnd, TVM_INSERTITEM, 0, (LPARAM)(LPTVINSERTSTRUCT)&tvins);
    
        tvi.pszText = TEXT("Item2");
        tvins.item = tvi;
        SendMessage(hTreeWnd, TVM_INSERTITEM, 0, (LPARAM)(LPTVINSERTSTRUCT)&tvins);
    
        tvi.stateMask = TVIS_CUT;
        tvi.state = TVIS_CUT;
        tvi.pszText = TEXT("Item3");
        tvins.item = tvi;
        SendMessage(hTreeWnd, TVM_INSERTITEM, 0, (LPARAM)(LPTVINSERTSTRUCT)&tvins);
    
        while (GetMessage(&msg, NULL, 0, 0)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    
        return msg.wParam;
    }
    
    LRESULT CALLBACK cbMain (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
        switch (message) {
            case WM_DESTROY: {
                PostQuitMessage (0);
            }
            break;
    
            case WM_NOTIFY: {
                NMHDR* pHdr = (LPNMHDR)lParam;
                if (pHdr->idFrom == IDC_TREEVIEW && pHdr->code == NM_CUSTOMDRAW) {
                    LPNMTVCUSTOMDRAW pCustomDraw = (LPNMTVCUSTOMDRAW)lParam;
                    if (pCustomDraw->nmcd.dwDrawStage == CDDS_PREPAINT)
                        return CDRF_NOTIFYITEMDRAW;
    
                    if (pCustomDraw->nmcd.dwDrawStage == CDDS_ITEMPREPAINT) {
                        BOOL isCut = TreeView_GetItemState(pHdr->hwndFrom, (HTREEITEM)pCustomDraw->nmcd.dwItemSpec, TVIS_CUT) & TVIS_CUT;
                        pCustomDraw->clrText = pCustomDraw->clrTextBk != RGB( 255, 255, 255) ? RGB( 255, 255, 255) :
                            isCut ? RGB( 128, 128, 128 ) : RGB( 0, 0, 0);
                    }
    
                    return CDRF_DODEFAULT;
                }
            }
            break;
    
            default:
                return DefWindowProc (hWnd, message, wParam, lParam);
        }
    
        return 0;
    }