Click here to Skip to main content
15,886,664 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I want to redraw syslistview32 , I mean each pixels. The content of client zone now is working well, but the non-client zone, the scrollbar, still flicker.
the flicker img

What I have tried:

I handled several messages,
WM_NCCALCSIZE: resize the non-client zone;

WM_NCPAINT/WM_NCACTIVATE: my owner drawing of scrollbar, and the border.

WM_ERASEBKGND: return TRUE;

WM_PAINT: I rewrite the painting procedure instead of using MS interface throw parent window WM_DRAWITEM.

WM_NCLBUTTONDOWN/WM_MOUSEMOVE/WM_NCLBUTTONUP: deal with scrolling window matters.

LVM_SCROLL: scrolling window , I use ScrollWindow function.

As following codes shows:
C++
LRESULT CALLBACK LVCProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
{
    pListViewStyle ls=ListView_GetSettings(hwnd);// this structure store some extra info. for my owner-drawing.
    if(!ls) return 0;
            
    switch(message){
    case WM_NCCALCSIZE: {
        return LVC_NCCalcSize(hwnd,wParam,lParam); // I adjust the scrollbar size
    } break;
    case WM_NCACTIVATE:
    case WM_NCPAINT: {
        LVC_NCDrawScrollBar(hwnd,SB_HORZ); // This function mainly implement the drawing for the scrollbar.
        LVC_NCDrawScrollBar(hwnd,SB_VERT);
        return 0;
    } break;
    case WM_NCMOUSEMOVE: return 1;
    case WM_ERASEBKGND: {
        return 1;
    } break;
    case WM_MOUSEWHEEL: {
        // support the scrolling for the listview when you scrolling the mouse-wheel.
        int wheel_delta=GET_WHEEL_DELTA_WPARAM(wParam);
        int scroll_pixls=((double)wheel_delta/10)*5*-1;
        SendMessage(hwnd,LVM_SCROLL,(WPARAM)0,(LPARAM)scroll_pixls);
        return 1;
    } break;
    case WM_PAINT: {
        // totally redraw the content of the control
        LVC_Paint(hwnd,wParam,lParam);
        return 0;
    } break;
    case WM_NCLBUTTONUP: 
        //WM_NCLBUTTONDON / WM_MOUSEMOVE / WM_NCLBUTTONUP , 
        //these three messages are handled when you press the thumb block and scrolling the window.
        ReleaseCapture();
    break;
    case WM_NCLBUTTONDOWN: {
        return LVC_ScrollBefore(hwnd,wParam,lParam);
    } break;
    case WM_MOUSEMOVE: {
        if(GetCapture()==hwnd) {
            LVC_Scrolling(hwnd,wParam,lParam);
            return 1;
        }
    } break;
    case WM_SIZE: {
        // update the nPage of SCROLLINFO for the control
        LVC_Size(hwnd,wParam,lParam);
        return 1;
    } break;
    case LVM_SCROLL: {
        // scroll the content and update the invalidate rect.
        LVC_Scroll(hwnd,wParam,lParam);
        return 1;
    } break;
    case WM_NCHITTEST: {
        // I change the return value for the scrollbar areas , which I abolish the two arrows area.
        return LVC_HitTest(hwnd,wParam,lParam);
    }
    case WM_LBUTTONDOWN:
        // when you click the item of the listview, you change the selected state for the item.
        LVC_ModifySelState(hwnd,wParam,lParam);
        return 0;
    } break;
    // this part has been canceled, 
    //I am not using the interface of owner-drawing each item provided by MS. 
    //I redraw directly by rewrite the WM_PAINT message.
    /*
    case WMYU_DRAWLISTVIEWITEM: {
        LVC_DrawItem(hwnd,wParam,lParam);
        return TRUE;
    } break;
    case WMYU_MEASURELISTVIEWITEM: {
        LPMEASUREITEMSTRUCT lpmis=(LPMEASUREITEMSTRUCT)lParam;
        lpmis->itemHeight=ls->content_height;
    } break;
    */
    case WM_NCDESTROY: {
        // release the extra resources before destroy the control.
        WNDPROC pre_proc=ls->pre_proc;
        ListView_ClearSettings(hwnd);
        if(pre_proc) return CallWindowProc(pre_proc,hwnd,message,wParam,lParam);
    } break;
    }
    
    // other messages do not change.
    return CallWindowProc(ls->pre_proc,hwnd,message,wParam,lParam);
}

/*WM_PAINT: redraw the whole content*/
int LVC_Paint(HWND hwnd,WPARAM wParam,LPARAM lParam)
{
    int idx_item=0;
    int page=0;
    int x_offset=0,y_offset=0;
    char item_text[256]="";//item text
    
    //control extra info.
    pListViewStyle ls=ListView_GetSettings(hwnd);
    if(!ls) return -1;
    
    //the sub-control head for the columns.   
    HWND head=ListView_GetHeader(hwnd);
    int itm_count=ListView_GetItemCount(hwnd);
    int col_count=Header_GetItemCount(head);
    int col_start=0,col_stop=col_count-1;
    RECT rcHead={0},rcClient;
    int head_height=0;
    GetWindowRect(head,&rcHead);
    head_height=rcHead.bottom-rcHead.top;
    GetClientRect(hwnd,&rcClient);
    
    //axis x,y offset . calculated by the SCROLLINFO provided by the control
    SCROLLINFO si={0};
    si.cbSize=sizeof(si);
    si.fMask=SIF_ALL;
    UINT style=GetWindowLongPtr(hwnd,GWL_STYLE);
    BOOL HasVScroll=((style&WS_VSCROLL)==WS_VSCROLL);
    BOOL HasHScroll=((style&WS_HSCROLL)==WS_HSCROLL);
    if(HasVScroll) {
        GetScrollInfo(hwnd,SB_VERT,&si);
        idx_item=si.nPos;
        page=si.nPage;
    }
    else page=ListView_GetItemCount(hwnd);
        
    if(HasHScroll) {
        GetScrollInfo(hwnd,SB_HORZ,&si);
        x_offset=si.nPos;
    }
    y_offset=head_height;
    
    //prepare for painting
    PAINTSTRUCT ps;
    HDC hdc=BeginPaint(hwnd,&ps);
    int cx=ps.rcPaint.right-ps.rcPaint.left;
    int cy=ps.rcPaint.bottom-ps.rcPaint.top;
    HDC memdc=CreateCompatibleDC(hdc);
    HBITMAP bmp=CreateCompatibleBitmap(hdc,cx,cy);
    HBITMAP pre_bmp=(HBITMAP)SelectObject(memdc,bmp);
    HFONT pre_font=(HFONT)SelectObject(memdc,(HFONT)SendMessage(hwnd,WM_GETFONT,0,0));
    SetBkMode(memdc,TRANSPARENT);
    SetTextColor(memdc,RGB(255,255,255));
    HPEN pen=CreatePen(PS_SOLID,1,RGB(40,46,44));
    HPEN pre_pen=(HPEN)SelectObject(memdc,pen);
    
    //memory rectangle for Mem DC
    RECT rcMem;
    CopyRect(&rcMem,&ps.rcPaint);
    
    //offset the coordinate
    POINT pt_org;
    OffsetViewportOrgEx(memdc,-rcMem.left,-rcMem.top,&pt_org);
    
    RECT rcItem;//item rectangle
    COLORREF rgb_curr;
    BOOL col_trigger=FALSE;//column painting stop flags. 
    int rcTop=0,rcBottom=0;
    
    //background
    HBRUSH bkbrush=CreateSolidBrush(RGB(20,20,20));
    FillRect(memdc,&rcMem,bkbrush);
    DeleteObject(bkbrush);
    
    //update the rectangle specfied by the PAINTSTRUCT
    for(int index=0;index<=page&&(index+idx_item<itm_count);index++) {
        //calculate whether each visible item in current view  is in invalid rect
        rcTop=index*ls->content_height+y_offset;
        rcBottom=rcTop+ls->content_height;
        
        //Item ID
        int itemID=index+idx_item;
        
        //if this item in rectangle specified by PAINTSTRUCT, redraw it.
        if(rcTop>=ps.rcPaint.top&&rcTop<=ps.rcPaint.bottom) {
            //state
            UINT state=ListView_GetItemState(hwnd,itemID,LVIS_SELECTED|LVIS_FOCUSED);
            
            //even  odd different color           
            if(itemID%2==0) rgb_curr=ls->rgb_even;
            else rgb_curr=ls->rgb_odd;
            if(state&LVIS_SELECTED==LVIS_SELECTED) rgb_curr=ls->rgb_sel;
            if(state&LVIS_FOCUSED==LVIS_FOCUSED) rgb_curr=ls->rgb_focus;
            HBRUSH brush=CreateSolidBrush(rgb_curr);
            HBRUSH pre_brush=(HBRUSH)SelectObject(memdc,brush);
            
            //draw item and subitem.
            for(int idx_col=col_start;idx_col<=col_stop;idx_col++) {  
                ListView_GetSubItemRect(hwnd,itemID,idx_col, LVIR_LABEL,&rcItem);
                //x,y axis offset
                OffsetRect(&rcItem,-x_offset,-idx_item*ls->content_height);
                
                //if current item or subitem not in update area, no need to hanle it
                if(ps.rcPaint.left>rcItem.right) continue;
                else if(!col_trigger){
                    col_start=idx_col;
                    col_trigger=TRUE;
                }
                if(ps.rcPaint.right<rcItem.left) {
                    col_stop=idx_col-1;
                    break;
                }
                    
                //while painting
                Rectangle(memdc,rcItem.left-1,rcItem.top-1,rcItem.right,rcItem.bottom);
                memset(item_text,0x00,sizeof(item_text));
                ListView_GetItemText(hwnd,itemID,idx_col,item_text,sizeof(item_text));
                InflateRect(&rcItem,-5,-3);
                DrawText(memdc,item_text,-1,&rcItem,DT_SINGLELINE|DT_VCENTER|DT_END_ELLIPSIS);
            }
            DeleteObject(brush);
        }
    }
    
    //paste 
    BitBlt(hdc,ps.rcPaint.left,ps.rcPaint.top,cx,cy,
           memdc,rcMem.left,rcMem.top,SRCCOPY);
    
    //reset
    SetViewportOrgEx(memdc,pt_org.x,pt_org.y,NULL);
    
    //resource reset and release
    SelectObject(memdc,pre_font);
    DeleteObject(SelectObject(memdc,pre_pen));
    DeleteObject(SelectObject(memdc,pre_bmp));
    DeleteDC(memdc);
    
    //paint finished
    EndPaint(hwnd,&ps);
    
    return 0;    
}

/*Paint the scrollbar*/
int LVC_NCDrawScrollBar(HWND hwnd,UINT scrolltype)
{
    RECT rc;
    RECT rcThumb;
    HDC hdc=GetWindowDC(hwnd);
    UINT style=(UINT)GetWindowLongPtr(hwnd,GWL_STYLE);
          
    //use memdc , double buffering. still flicker  
    if(scrolltype==SB_VERT) {
        if(LVC_GetZoneRect(hwnd,ZVSCROLL,&rc,TRUE)==0&&
           LVC_GetZoneRect(hwnd,ZVSTHUMB,&rcThumb,TRUE)==0) {
            int cx=rc.right-rc.left,cy=rc.bottom-rc.top;
           
            if((style&WS_HSCROLL)) cy+=SCROLLBAR_PIXLS;
           
            HDC memdc=CreateCompatibleDC(hdc);
            HBITMAP bmp=CreateCompatibleBitmap(hdc,cx,cy); 
            HBITMAP pre_bmp=(HBITMAP)SelectObject(memdc,bmp);
           
            RECT rcMem;
            CopyRect(&rcMem,&rc);
            OffsetRect(&rcMem,-rcMem.left,-rcMem.top);
            
            HBRUSH brush=CreateSolidBrush(RGB(15,15,15));
            FillRect(memdc,&rcMem,brush);
            DeleteObject(brush);
           
            OffsetRect(&rcThumb,-rc.left-1,-rc.top);
            InflateRect(&rcThumb,-1,0);
            Graphics graphic(memdc);
            GraphicsPath path;
            LinearGradientBrush pbrush(Rect(rcThumb.left,rcThumb.top,rcThumb.right-rcThumb.left,rcThumb.bottom-rcThumb.top),
                                       Color(255,50,50,50),
                                       Color(255,20,20,20),
                                       LinearGradientModeHorizontal);
            
            graphic.SetSmoothingMode(SmoothingModeHighQuality);
            path.AddArc(rcThumb.left,rcThumb.top,SCROLLBAR_PIXLS,SCROLLBAR_PIXLS,180,180);
            path.AddArc(rcThumb.left,rcThumb.bottom-SCROLLBAR_PIXLS,SCROLLBAR_PIXLS,SCROLLBAR_PIXLS,0,180);
            graphic.FillPath(&pbrush,&path);
            
            BitBlt(hdc,rc.left,rc.top,cx,cy,memdc,0,0,SRCCOPY);
            
            DeleteObject(SelectObject(memdc,pre_bmp));
            DeleteDC(memdc);
        }
    }
    else if(scrolltype==SB_HORZ) { 
        if(LVC_GetZoneRect(hwnd,ZHSCROLL,&rc,TRUE)==0&&
           LVC_GetZoneRect(hwnd,ZHSTHUMB,&rcThumb,TRUE)==0) {
            int cx=rc.right-rc.left,cy=rc.bottom-rc.top;
           
            HDC memdc=CreateCompatibleDC(hdc);
            HBITMAP bmp=CreateCompatibleBitmap(hdc,cx,cy); 
            HBITMAP pre_bmp=(HBITMAP)SelectObject(memdc,bmp);
           
            RECT rcMem;
            CopyRect(&rcMem,&rc);
            OffsetRect(&rcMem,-rcMem.left,-rcMem.top);
            HBRUSH brush=CreateSolidBrush(RGB(15,15,15));
            FillRect(memdc,&rcMem,brush);
            DeleteObject(brush);
           
            OffsetRect(&rcThumb,-rc.left,-rc.top-1);
            InflateRect(&rcThumb,0,-1);
            Graphics graphic(memdc);
            GraphicsPath path;
            LinearGradientBrush pbrush(Rect(rcThumb.left,rcThumb.top,rcThumb.right-rcThumb.left,rcThumb.bottom-rcThumb.top),
                                       Color(255,50,50,50),
                                       Color(255,20,20,20),
                                       LinearGradientModeVertical);
            
            graphic.SetSmoothingMode(SmoothingModeHighQuality);
            path.AddArc(rcThumb.left,rcThumb.top,SCROLLBAR_PIXLS,SCROLLBAR_PIXLS,90,180);
            path.AddArc(rcThumb.right-SCROLLBAR_PIXLS,rcThumb.top,SCROLLBAR_PIXLS,SCROLLBAR_PIXLS,-90,180);
            graphic.FillPath(&pbrush,&path);
            
            BitBlt(hdc,rc.left,rc.top,cx,cy,memdc,0,0,SRCCOPY);
            
            DeleteObject(SelectObject(memdc,pre_bmp));
            DeleteDC(memdc);
        }
    }

    ReleaseDC(hwnd,hdc);
    return 0;
}

/* LVM_SCROLL*/
int LVC_Scroll(HWND hwnd,WPARAM wParam,LPARAM lParam)
{
    HWND head=ListView_GetHeader(hwnd);
    if(!head) return 0;
    pListViewStyle ls=ListView_GetSettings(hwnd);
    if(!ls) return 0;
        
    int hscroll_pixls=(int)wParam;
    int vscroll_pixls=(int)lParam;
    RECT rc,rcScroll,rcHead,rcInvalid;
    GetClientRect(hwnd,&rc);
    CopyRect(&rcScroll,&rc);
    
    GetWindowRect(head,&rcHead);
    rcScroll.top=rcHead.bottom-rcHead.top;
    CopyRect(&rcInvalid,&rcScroll);
    
    SCROLLINFO si={0};
    si.cbSize=sizeof(SCROLLINFO);
    si.fMask=SIF_ALL;
    if(hscroll_pixls!=0) {
        GetScrollInfo(hwnd,SB_HORZ,&si);
        int pre_pos=si.nPos;
        si.fMask=SIF_POS;
        si.nPos=pre_pos+hscroll_pixls;
        if(si.nPos+si.nPage>=si.nMax) si.nPos=si.nMax-si.nPage+1;
        else if(si.nPos<=si.nMin) si.nPos=si.nMin;
        SetScrollInfo(hwnd,SB_HORZ,&si,TRUE);
        InvalidateRect(head,NULL,TRUE);
        if(hscroll_pixls>0) rcInvalid.left=rcInvalid.right-hscroll_pixls-1;
        else rcInvalid.right=rcInvalid.left-hscroll_pixls+1;
        ScrollWindow(hwnd,-hscroll_pixls,0,NULL,&rcScroll);
        LVC_NCDrawScrollBar(hwnd,SB_HORZ);
        InvalidateRect(hwnd,&rcInvalid,TRUE);
    }
    if(vscroll_pixls!=0) {
        GetScrollInfo(hwnd,SB_VERT,&si);
        int pre_pos=si.nPos;
        int page=si.nPage;
        si.fMask=SIF_POS;
        int scroll_pos=vscroll_pixls/ls->content_height;
        //recalculate the nPos.
        si.nPos=pre_pos+scroll_pos;
        if(si.nPos+si.nPage>=si.nMax) si.nPos=si.nMax-si.nPage+1;
        else if(si.nPos<=si.nMin) si.nPos=si.nMin;
        vscroll_pixls=(si.nPos-pre_pos)*ls->content_height;
        SetScrollInfo(hwnd,SB_VERT,&si,TRUE);
        if(vscroll_pixls>0)
            rcInvalid.bottom=rcInvalid.top+vscroll_pixls;
        else
            rcInvalid.top=rcInvalid.top+(page+scroll_pos)*ls->content_height-1;
        if(rcInvalid.top<rcScroll.top) rcInvalid.top=rcScroll.top;
        if(rcInvalid.bottom>rcScroll.bottom) rcInvalid.bottom=rcScroll.bottom;
        ScrollWindow(hwnd,0,-vscroll_pixls,NULL,&rcScroll);
        InvalidateRect(hwnd,&rcInvalid,TRUE);
        LVC_NCDrawScrollBar(hwnd,SB_VERT);
    }
    return 0;
}

int LVC_ScrollBefore(HWND hwnd,WPARAM wParam,LPARAM lParam)
{
    UINT hit=wParam;
    POINT pt={GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam)};
    pListViewStyle ls=ListView_GetSettings(hwnd);
    if(!ls) return -1;
    
    if(hit==HTHSCROLL) {
        RECT rc_hs;
        if(LVC_GetZoneRect(hwnd,ZHSTHUMB,&rc_hs,FALSE)!=0) return -1;
        if(!PtInRect(&rc_hs,pt)) return -1;
        
        ScreenToClient(hwnd,&pt);
        ls->drag_pt.x=pt.x;
        ls->drag_pt.y=-1;
        
        SCROLLINFO si={0};
        si.cbSize=sizeof(si);
        si.fMask=SIF_POS;
        GetScrollInfo(hwnd,SB_HORZ,&si);
        
        ls->drag_pos.x=si.nPos;
        ls->drag_pos.y=-1;    
        
        SetCapture(hwnd);
        return 0;
    }
    else if(hit==HTVSCROLL) {
        RECT rc_vs;
        if(LVC_GetZoneRect(hwnd,ZVSTHUMB,&rc_vs,FALSE)!=0) return -1;
        if(!PtInRect(&rc_vs,pt)) return -1;
        
        ScreenToClient(hwnd,&pt);
        ls->drag_pt.y=pt.y;
        ls->drag_pt.x=-1;
        
        SCROLLINFO si={0};
        si.cbSize=sizeof(si);
        si.fMask=SIF_POS;
        GetScrollInfo(hwnd,SB_VERT,&si);
        
        ls->drag_pos.y=si.nPos;
        ls->drag_pos.x=-1;    
                    
        SetCapture(hwnd);
        return 0;
    }
    else {
        return CallWindowProc(ls->pre_proc,hwnd,WM_NCLBUTTONDOWN,wParam,lParam);
    }
}

int LVC_Scrolling(HWND hwnd,WPARAM wParam,LPARAM lParam)
{
    POINT pt={GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam)};
    pListViewStyle ls=ListView_GetSettings(hwnd);
    if(!ls) return -1;
        
    if(GetCapture()!=hwnd) return -1;
    if(ls->drag_pos.x!=-1) { // HORZ
        RECT rc_hs,rc_thumb;
        
        if(LVC_GetZoneRect(hwnd,ZHSCROLL,&rc_hs,TRUE)!=0) return -1;
        if(LVC_GetZoneRect(hwnd,ZHSTHUMB,&rc_thumb,TRUE)!=0) return -1;
        
        SCROLLINFO si;
        si.cbSize=sizeof(si);
        si.fMask=SIF_ALL;
        GetScrollInfo(hwnd,SB_HORZ,&si);
        int pre_pos=si.nPos;
        si.nPos=(int)ls->drag_pos.x+(int)(si.nMax-si.nMin+1-si.nPage)*1.0*(int)(pt.x-ls->drag_pt.x)/((rc_hs.right-rc_hs.left)-(rc_thumb.right-rc_thumb.left));   
        if(si.nPos==ls->drag_pos.x) return -1;
        
        if((int)si.nPos>(int)(si.nMax-si.nPage+1)) si.nPos=si.nMax-si.nPage+1;
        else if(si.nPos<si.nMin) si.nPos=si.nMin;
        
        ListView_Scroll(hwnd,si.nPos-pre_pos,0);
        LVC_NCDrawScrollBar(hwnd,SB_HORZ);
    }
    else if(ls->drag_pos.y!=-1) {
        RECT rc_vs,rc_thumb;
        
        if(LVC_GetZoneRect(hwnd,ZVSCROLL,&rc_vs,TRUE)!=0) return -1;
        if(LVC_GetZoneRect(hwnd,ZVSTHUMB,&rc_thumb,TRUE)!=0) return -1;
        
        SCROLLINFO si;
        si.cbSize=sizeof(si);
        si.fMask=SIF_ALL;
        GetScrollInfo(hwnd,SB_VERT,&si);
        int pre_pos=si.nPos;
        
        si.nPos=(int)ls->drag_pos.y+((int)(si.nMax-si.nMin+1-si.nPage))*1.0*((int)(pt.y-ls->drag_pt.y))/((rc_vs.bottom-rc_vs.top)-(rc_thumb.bottom-rc_thumb.top));   
        if(si.nPos==ls->drag_pos.y) return -1;
        
        if((int)si.nPos>(int)(si.nMax-si.nPage+1)) si.nPos=si.nMax-si.nPage+1;
        else if(si.nPos<si.nMin) si.nPos=si.nMin;
        
        ListView_Scroll(hwnd,0,(si.nPos-pre_pos)*ls->content_height);
        LVC_NCDrawScrollBar(hwnd,SB_VERT);
    }
    return 0;
}

int LVC_ScrollDone(HWND hwnd,WPARAM wParam,LPARAM lParam)
{
    pListViewStyle ls=ListView_GetSettings(hwnd);
    if(!ls) return -1;
    
    if(GetCapture()==hwnd) {
        ReleaseCapture();
        if(ls->drag_pos.y!=-1) LVC_NCDrawScrollBar(hwnd,SB_VERT);
        if(ls->drag_pos.x!=-1) LVC_NCDrawScrollBar(hwnd,SB_HORZ);
        
        ls->drag_pt.x=ls->drag_pt.y=ls->drag_pos.x=ls->drag_pos.y=-1;            
    }
    
    return 0;
}
Posted
Updated 8-Nov-21 20:53pm

1 solution

One thing I noticed in the video is it seems to flicker when it hits the scrollbar hits the far, right-hand edge. I think that is a key point to the problem. Have you used Spy++ to monitor which messages are being sent? It might take a while to get the filters set right but it is very helpful once you have. You can also override the default window procedure and write a message tracer. That can also be a big help.

Another thing you might want to do is look at how a custom scroll bar is implemented. Here is one that handles 64-bit scroll values : Hex Control for C++/Win32 applications.[^]. The article is about a custom control and might provide some useful tips.
 
Share this answer
 
Comments
Yount_0701 9-Nov-21 4:02am    
spy++ doesn't work. I give up.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900