简体   繁体   中英

Unable to receive all NM_CUSTOMDRAW dwDrawStage for a ListView

I have a Win32 application (no mfc) with MDI frame window.

The main MDI frame window will of cause has it MDICLIENT window. But in my application, the main frame window has another tabctrl child window, which displays some listview wnds on the bottom of the main frame:

//create the tab first, as a child of main MDI frame with 800px width and 300px height, at point (0, 600) of main frame window.
HWND hWndTabCtrl = CreateWindow(WC_TABCONTROL, NULL, WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE | TCS_BOTTOM, 0, 600, 800, 300, hWndMainFrame, NULL, hInstance, NULL);

//add one tab item for test:
TCITEM item;
item.mask = TCIF_TEXT;
item.pszText = L"Test List View";
TabCtrl_InsertItem(hWndTabCtrl , 0, &item);

//And then create a listview, this list view is a child window of tab ctrl (hWndTabCtrl)
HWND hWndListView = CreateWindow(WC_LISTVIEW, L"", WS_CHILD | WS_BORDER | LVS_REPORT | LVS_SHOWSELALWAYS | WS_VISIBLE, 0, 0,800, 280, hWndTabCtrl , NULL, hInstance, NULL);

//Now insert two columns:
LVCOLUMN column;
column.mask = LVCF_WIDTH | LVCF_TEXT;
column.cx = 200;
column.pszText = L"Column 0";
ListView_InsertColumn(hWndListView , 0, &column); //column for sub item 0
column.pszText = L"Column 1";
ListView_InsertColumn(hWndListView , 1, &column); //column for sub item 1

Now I want to change text color for sub item 1.

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
    switch (message){
        case WM_NOTIFY:
            if (((LPNMHDR)lParam)->hwndFrom == hWndListView && ((LPNMHDR)lParam)->code == NM_CUSTOMDRAW){

                LPNMLVCUSTOMDRAW lplvcd = (LPNMLVCUSTOMDRAW)lParam;

                switch (lplvcd->nmcd.dwDrawStage){
                    case CDDS_PREPAINT:
                        //I can receive this value 
                        return CDRF_NOTIFYITEMDRAW;
                        break;
                    case CDDS_ITEMPREPAINT:
                        //I cann't receive this value:
                        return CDRF_NOTIFYSUBITEMDRAW;
                        break;
                    case CDDS_SUBITEM | CDDS_ITEMPREPAINT:
                        //I cann't receive this value:
                        lplvcd->clrText = RGB(255,0, 0);
                        return CDRF_NEWFONT;
                        break;
                }
                return CDRF_DODEFAULT;
            }
            break;
        case ...
            ...
            break;
    default:
    return DefFrameProc(hWnd, hWndMainFrame, message, wParam, lParam);

    }
}

In the case WM_NOTIFY code block, I can only receive lplvcd->nmcd.dwDrawStage once, which value is CDDS_PREPAINT. I believe it is because the return value CDRF_NOTIFYITEMDRAW was not return to correct "parent".

Is it a problem of TabCtrl or a problem of MDI window?

Despite what the documentation says, the return value is actually a bitmask so you can return multiple values at a time. Likewise, the dwDrawingStage is also a bitmask as well, so you should be looking for specific bits you are interested in.

Try something more like this:

LPNMHDR pnmhdr = (LPNMHDR) lParam;
if ((pnmhdr->hwndFrom == hWndListView) && (pnmhdr->code == NM_CUSTOMDRAW))
{
    LPNMLVCUSTOMDRAW lplvcd = (LPNMLVCUSTOMDRAW) lParam;

    if (lplvcd->nmcd.dwDrawStage & CDDS_ITEM)
    {
        if (lplvcd->nmcd.dwDrawStage & CDDS_ITEMPREPAINT)
        {
            if (lplvcd->nmcd.dwDrawStage & CDDS_SUBITEM)
            {
                lplvcd->clrText = RGB(255,0, 0);
                ...
                return CDRF_DODEFAULT | CDRF_NEWFONT;
            }
            else
            {
                ...
                return CDRF_DODEFAULT | CDRF_NOTIFYSUBITEMDRAW;
            }
        }
        ...
    }
    else
    {
        switch (lplvcd->nmcd.dwDrawStage)
        {
            case CDDS_PREPAINT:
            {
                ...
                return CDRF_NOTIFYITEMDRAW | CDRF_NOTIFYSUBITEMDRAW;
            }
            ...
        }
    }
    return CDRF_DODEFAULT;
}

I had a similar problem (a ListView in a Dialog), with a common factor: no MFC. Same outcome: receiving CDDS_PREPAINT, but not CDDS_ITEMPREPAINT nor its combinations (CDDS_SUBITEM | CDDS_PREPAINT and CDDS_ITEM | CDDS_SUBITEM | CDDS_PREPAINT). After 3 days of digging I found the answer in https://www.codeproject.com/Articles/2890/Using-ListView-control-under-Win-API .

In my DialogProc I was returning the NM_CUSTOMDRAW's result to WM_NOTIFY, something like this:

    case WM_NOTIFY:
        NMHDR* pHdr = (NMHDR*) lParam;
        if (pHdr->idFrom == IDC_LIST_RESULTS  &&  pHdr->code == NM_CUSTOMDRAW)
        {
            return HandleCustomDraw((NMLVCUSTOMDRAW*)pHdr);
        }
        break;

But the correct behaviour is to send the result to the owner:

    case WM_NOTIFY:
        NMHDR* pHdr = (NMHDR*) lParam;
        if (pHdr->idFrom == IDC_LIST_RESULTS  &&  pHdr->code == NM_CUSTOMDRAW)
        {
            SetWindowLong(hDlg, DWL_MSGRESULT, (LONG)HandleCustomDraw((NMLVCUSTOMDRAW*)pHdr));
            return TRUE;
        }
        break;

Hope it helps!

Edit: and of course now I found the same answer already given in another stackoverflow question :-) Virtual listview doesn't get CDDS_ITEMPREPAINT c++

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM