标准的滚动条 是不能通过子类化来制定的, 如果想得到漂亮的滚动条,需要自己动手画。
要为CListCtrl 添加滚动条,大概思路如下:
(1) 使 CListCtrl 标准的滚动条不可用
(2) 手动定位 H 和 V 滚动条的位置( 不属于 ClistCtrl 的子控件 )
(3) 需要处理,
鼠标点击 左右箭头, 滚动一行
鼠标点击 滑块空白处, 滚动一页
鼠标拖拽滑块
下面是简单示例图: 滑块,左右箭头均是 宽26像素 高12像素
分析代码如下 SkinHorizontalScrollbar.cpp
// SkinHorizontalScrollbar.cpp : implementation file // #include "stdafx.h" #include "test_scroll.h" #include "SkinHorizontalScrollbar.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // CSkinHorizontalScrollbar CSkinHorizontalScrollbar::CSkinHorizontalScrollbar() { nThumbLeft = 25; dbThumbRemainder = 0.00f; bMouseDown = false; bMouseDownArrowLeft = false; bMouseDownArrowRight = false; bDragging = false; pList = NULL; } CSkinHorizontalScrollbar::~CSkinHorizontalScrollbar() { } BEGIN_MESSAGE_MAP(CSkinHorizontalScrollbar, CStatic) //{{AFX_MSG_MAP(CSkinHorizontalScrollbar) ON_WM_PAINT() ON_WM_LBUTTONDOWN() ON_WM_LBUTTONUP() ON_WM_MOUSEMOVE() ON_WM_TIMER() //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CSkinHorizontalScrollbar message handlers void CSkinHorizontalScrollbar::OnPaint() { CPaintDC dc(this); // device context for painting Draw(); } void CSkinHorizontalScrollbar::OnLButtonDown(UINT nFlags, CPoint point) { SetCapture(); CRect clientRect; GetClientRect(&clientRect); int nWidth = clientRect.Width()-26; // 左箭头的区域 CRect rectLeftArrow(0,0,26,20); // 右箭头区域 CRect rectRightArrow(nWidth,0,nWidth+26,20); // 滑块区域 CRect rectThumb(nThumbLeft,0,nThumbLeft+26,20); // 是否点重滑块 if(rectThumb.PtInRect(point)) { bMouseDown = true; } // 是否点重右箭头 if(rectRightArrow.PtInRect(point)) { bMouseDownArrowRight = true; SetTimer(2,250,NULL); } // 是否点重左箭头 if(rectLeftArrow.PtInRect(point)) { bMouseDownArrowLeft = true; SetTimer(2,250,NULL); } CStatic::OnLButtonDown(nFlags, point); } void CSkinHorizontalScrollbar::OnLButtonUp(UINT nFlags, CPoint point) { UpdateThumbPosition(); KillTimer(1); ReleaseCapture(); bool bInChannel = true; CRect clientRect; GetClientRect(&clientRect); int nWidth = clientRect.Width()-26; CRect rectLeftArrow(0,0,26,20); CRect rectThumb(nThumbLeft,0,nThumbLeft+26,20); if(rectLeftArrow.PtInRect(point)) { ScrollLeft(); bInChannel = false; } CRect rectRightArrow(nWidth,0,nWidth+26,20); if(rectRightArrow.PtInRect(point)) { ScrollRight(); bInChannel = false; } if(rectThumb.PtInRect(point)) { bInChannel = false; } if(bInChannel == true && !bMouseDown) { if(point.x > nThumbLeft) { PageRight(); } else { PageLeft(); } } //恢复所有变量 bMouseDown = false; bDragging = false; bMouseDownArrowLeft = false; bMouseDownArrowRight = false; CStatic::OnLButtonUp(nFlags, point); } void CSkinHorizontalScrollbar::OnMouseMove(UINT nFlags, CPoint point) { CRect clientRect; GetClientRect(&clientRect); if(bMouseDown) bDragging = true; if(bDragging) { //滑块宽度 26 , 此处假定鼠标点中的是滑块的中点, 计算滑块位置 nThumbLeft = point.x-13; double nMax = pList->GetScrollLimit(SB_HORZ); int nPos = pList->GetScrollPos(SB_HORZ); // 计算可滚动的范围: clientRect.Width() - 左箭头宽 - 右箭头宽 - 滑块宽 double nWidth = clientRect.Width()-75; double nVar = nMax; dbThumbInterval = nWidth/nVar; // nThumbLeft-25: 滑块偏离起点的距离 // (nThumbLeft-25)/dbThumbInterval 需要滚动的次数 int nScrollTimes = (int)((nThumbLeft-25)/dbThumbInterval)-nPos; CSize size; size.cx = nScrollTimes; size.cy = 0; // 强制列表滚动 pList->Scroll(size); LimitThumbPosition(); Draw(); } CStatic::OnMouseMove(nFlags, point); } void CSkinHorizontalScrollbar::OnTimer(UINT nIDEvent) { if(nIDEvent == 1) { if(bMouseDownArrowRight) { ScrollRight(); } if(bMouseDownArrowLeft) { ScrollLeft(); } } else if(nIDEvent == 2) { if(bMouseDownArrowRight) { KillTimer(2); SetTimer(1, 50, NULL); } if(bMouseDownArrowLeft) { KillTimer(2); SetTimer(1, 50, NULL); } } CStatic::OnTimer(nIDEvent); } void CSkinHorizontalScrollbar::ScrollLeft() { pList->SendMessage(WM_HSCROLL, MAKELONG(SB_LINELEFT,0),NULL); UpdateThumbPosition(); } void CSkinHorizontalScrollbar::ScrollRight() { pList->SendMessage(WM_HSCROLL, MAKELONG(SB_LINERIGHT,0),NULL); UpdateThumbPosition(); } void CSkinHorizontalScrollbar::UpdateThumbPosition() { CRect clientRect; GetClientRect(&clientRect); double nPos = pList->GetScrollPos(SB_HORZ); double nMax = pList->GetScrollLimit(SB_HORZ); double nWidth = clientRect.Width()-75; double nVar = nMax; dbThumbInterval = nWidth/nVar; double nNewdbValue = dbThumbInterval * (nPos); int nNewValue = (int)nNewdbValue; //double nExtra = nNewdbValue - nNewValue; //dbThumbRemainder = nExtra; nThumbLeft = 25+nNewValue; TRACE("nPos: %0.3f, nMax : %0.3f, nWidth: %0.3f, dbThumbInterval: %0.3f/r/n", nPos , nMax, nWidth, dbThumbInterval); LimitThumbPosition(); Draw(); } void CSkinHorizontalScrollbar::PageRight() { TRACE("CSkinHorizontalScrollbar::PageRight /r/n"); pList->SendMessage(WM_HSCROLL, MAKELONG(SB_PAGEDOWN,0),NULL); UpdateThumbPosition(); } void CSkinHorizontalScrollbar::PageLeft() { TRACE("CSkinHorizontalScrollbar::PageLeft /r/n"); pList->SendMessage(WM_HSCROLL, MAKELONG(SB_PAGEUP,0),NULL); UpdateThumbPosition(); } void CSkinHorizontalScrollbar::Draw() { CClientDC dc(this); CRect clientRect; GetClientRect(&clientRect); CMemDC memDC(&dc, &clientRect); // 填充背景 memDC.FillSolidRect(&clientRect, RGB(76,85,118)); //画左箭头 CDC bitmapDC; bitmapDC.CreateCompatibleDC(&dc); CBitmap bitmap; bitmap.LoadBitmap(IDB_HORIZONTAL_SCROLLBAR_LEFTARROW); CBitmap* pOldBitmap = bitmapDC.SelectObject(&bitmap); memDC.BitBlt(clientRect.left,clientRect.top,26,12,&bitmapDC,0,0,SRCCOPY); bitmapDC.SelectObject(pOldBitmap); bitmap.DeleteObject(); pOldBitmap = NULL; //画中间背景 bitmap.LoadBitmap(IDB_HORIZONTAL_SCROLLBAR_SPAN); pOldBitmap = bitmapDC.SelectObject(&bitmap); int nWidth = clientRect.Width() - 26; memDC.StretchBlt(clientRect.left+26, clientRect.top, nWidth,12,&bitmapDC, 0,0, 1, 12, SRCCOPY); bitmapDC.SelectObject(pOldBitmap); bitmap.DeleteObject(); pOldBitmap = NULL; //画右箭头 bitmap.LoadBitmap(IDB_HORIZONTAL_SCROLLBAR_RIGHTARROW); pOldBitmap = bitmapDC.SelectObject(&bitmap); memDC.BitBlt(nWidth,clientRect.top,26,12,&bitmapDC,0,0,SRCCOPY); bitmapDC.SelectObject(pOldBitmap); bitmap.DeleteObject(); pOldBitmap = NULL; //If there is nothing to scroll then don't //show the thumb control otherwise show it TRACE("pList->GetScrollLimit: %d/r/n", pList->GetScrollLimit(SB_HORZ) ); if(pList->GetScrollLimit(SB_HORZ) != 0) { bitmap.LoadBitmap(IDB_HORIZONTAL_SCROLLBAR_THUMB); pOldBitmap = bitmapDC.SelectObject(&bitmap); memDC.BitBlt( clientRect.left+nThumbLeft, clientRect.top, 26,12, &bitmapDC, 0, 0, SRCCOPY); bitmapDC.SelectObject(pOldBitmap); bitmap.DeleteObject(); pOldBitmap = NULL; } } void CSkinHorizontalScrollbar::LimitThumbPosition() { CRect clientRect; GetClientRect(&clientRect); // 滑块的范围是 左右箭头之间的区域, 左右箭头宽 26 像素 if(nThumbLeft+26 > (clientRect.Width()-26)) { nThumbLeft = clientRect.Width()-51; } if(nThumbLeft < (clientRect.left+25)) { nThumbLeft = clientRect.left+25; } }
SkinHorizontalScrollbar.h
#if !defined(AFX_SKINHORIZONTALSCROLLBAR_H__77B6A7DF_1670_44D6_AA66_28424AF219DB__INCLUDED_) #define AFX_SKINHORIZONTALSCROLLBAR_H__77B6A7DF_1670_44D6_AA66_28424AF219DB__INCLUDED_ #include "memdc.h" class CSkinHorizontalScrollbar : public CStatic { public: CSkinHorizontalScrollbar(); void ScrollLeft(); void ScrollRight(); bool bMouseDownArrowRight, bMouseDownArrowLeft; bool bDragging; bool bMouseDown; int nThumbLeft; double dbThumbRemainder; double dbThumbInterval; // Attributes public: // Operations public: public: CListCtrl* pList; void LimitThumbPosition(); void Draw(); void PageLeft(); void PageRight(); void UpdateThumbPosition(); virtual ~CSkinHorizontalScrollbar(); protected: afx_msg void OnPaint(); afx_msg void OnLButtonDown(UINT nFlags, CPoint point); afx_msg void OnLButtonUp(UINT nFlags, CPoint point); afx_msg void OnMouseMove(UINT nFlags, CPoint point); afx_msg void OnTimer(UINT nIDEvent); DECLARE_MESSAGE_MAP() }; #endif
CMyListCtrl::Init 动态创建滚动条控件, 并定位滚动条的位置。
然后 在CDialog::OnInitDialog() 点调用 m_list.Init 即可完成。