windows - 我用双缓冲重绘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;
}
解决方案
推荐阅读
- spring - 我们是否在 Spring 中提供了拦截器链接?
- javascript - 使用 jquery 显示带有 optgroup 选项名称的下拉值
- php - 如何在 php 中格式化 json 响应,然后在 js 文件中回显它
- java - 为什么 Kotlin sortBy() 似乎以相反的顺序运行?
- ruby-on-rails - true:TrueClass 的未定义方法“课程”
- r - 将置信区间添加到 R 中的摘要输出
- regex - 如何使用正则表达式在特定字符后获取 1 或 2 个字符?
- javascript - 使元素在另外两个类之间切换的简单而优雅的方法
- python - 在 Amazon Web Services 上创建 cron 作业的可扩展方式是什么?
- css - 滚动条宽度:无;VS 溢出-x:隐藏