首页 > 解决方案 > 我用双缓冲重绘SysListView32控件的滚动条,为什么还是闪烁?

问题描述

麻烦

看图片,当我滚动垂直滚动条时,有一些闪烁。我想应该处理一些我错过的其他消息。WM_NCPAINT已按我自己的程序处理。闪烁似乎是原始滚动条绘图已被触发,我不知道在哪里。 在此处输入图像描述

我做了什么

我重绘了 SysListView32 控件,包括客户端区域和非客户端区域。正如您在图片中看到的那样,我在非客户区重绘滚动条。我使用内存DC进行双缓冲绘图,希望没有闪烁问题,但不成功。

我将几条消息处理为以下代码:

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;
}

标签: windowswinapi

解决方案


推荐阅读