标准的滚动条 是不能通过子类化来制定的, 如果想得到漂亮的滚动条,需要自己动手画。
要为CListCtrl 添加滚动条,大概思路如下:
(1) 使 CListCtrl 标准的滚动条不可用
(2) 手动定位 H 和 V 滚动条的位置( 不属于 ClistCtrl 的子控件 )
(3) 需要处理,
鼠标点击 左右箭头, 滚动一行
鼠标点击 滑块空白处, 滚动一页
鼠标拖拽滑块
下面是简单示例图: 滑块,左右箭头均是 宽26像素 高12像素
分析代码如下 SkinHorizontalScrollbar.cpp
view plaincopy to clipboardprint?
1.// SkinHorizontalScrollbar.cpp : implementation file
2.//
3.
4.#include "stdafx.h"
5.#include "test_scroll.h"
6.#include "SkinHorizontalScrollbar.h"
7.
8.#ifdef _DEBUG
9.#define new DEBUG_NEW
10.#undef THIS_FILE
11.static char THIS_FILE[] = __FILE__;
12.#endif
13.
14./
15.// CSkinHorizontalScrollbar
16.
17.CSkinHorizontalScrollbar::CSkinHorizontalScrollbar()
18.{
19. nThumbLeft = 25;
20. dbThumbRemainder = 0.00f;
21.
22. bMouseDown = false;
23. bMouseDownArrowLeft = false;
24. bMouseDownArrowRight = false;
25. bDragging = false;
26. pList = NULL;
27.}
28.
29.CSkinHorizontalScrollbar::~CSkinHorizontalScrollbar()
30.{
31.}
32.
33.
34.BEGIN_MESSAGE_MAP(CSkinHorizontalScrollbar, CStatic)
35. //{{AFX_MSG_MAP(CSkinHorizontalScrollbar)
36. ON_WM_PAINT()
37. ON_WM_LBUTTONDOWN()
38. ON_WM_LBUTTONUP()
39. ON_WM_MOUSEMOVE()
40. ON_WM_TIMER()
41. //}}AFX_MSG_MAP
42.END_MESSAGE_MAP()
43.
44./
45.// CSkinHorizontalScrollbar message handlers
46.
47.void CSkinHorizontalScrollbar::OnPaint()
48.{
49. CPaintDC dc(this); // device context for painting
50.
51. Draw();
52.}
53.
54.void CSkinHorizontalScrollbar::OnLButtonDown(UINT nFlags, CPoint point)
55.{
56. SetCapture();
57. CRect clientRect;
58. GetClientRect(&clientRect);
59.
60. int nWidth = clientRect.Width()-26;
61.
62. // 左箭头的区域
63. CRect rectLeftArrow(0,0,26,20);
64. // 右箭头区域
65. CRect rectRightArrow(nWidth,0,nWidth+26,20);
66. // 滑块区域
67. CRect rectThumb(nThumbLeft,0,nThumbLeft+26,20);
68.
69.
70. // 是否点重滑块
71. if(rectThumb.PtInRect(point))
72. {
73. bMouseDown = true;
74. }
75.
76. // 是否点重右箭头
77. if(rectRightArrow.PtInRect(point))
78. {
79. bMouseDownArrowRight = true;
80. SetTimer(2,250,NULL);
81. }
82. // 是否点重左箭头
83. if(rectLeftArrow.PtInRect(point))
84. {
85. bMouseDownArrowLeft = true;
86. SetTimer(2,250,NULL);
87. }
88.
89. CStatic::OnLButtonDown(nFlags, point);
90.}
91.
92.void CSkinHorizontalScrollbar::OnLButtonUp(UINT nFlags, CPoint point)
93.{
94. UpdateThumbPosition();
95. KillTimer(1);
96. ReleaseCapture();
97.
98.
99. bool bInChannel = true;
100.
101. CRect clientRect;
102. GetClientRect(&clientRect);
103.
104. int nWidth = clientRect.Width()-26;
105.
106. CRect rectLeftArrow(0,0,26,20);
107. CRect rectThumb(nThumbLeft,0,nThumbLeft+26,20);
108.
109. if(rectLeftArrow.PtInRect(point))
110. {
111. ScrollLeft();
112. bInChannel = false;
113. }
114.
115. CRect rectRightArrow(nWidth,0,nWidth+26,20);
116.
117.
118. if(rectRightArrow.PtInRect(point))
119. {
120. ScrollRight();
121. bInChannel = false;
122. }
123.
124. if(rectThumb.PtInRect(point))
125. {
126. bInChannel = false;
127. }
128.
129. if(bInChannel == true && !bMouseDown)
130. {
131. if(point.x > nThumbLeft)
132. {
133. PageRight();
134. }
135. else
136. {
137. PageLeft();
138. }
139. }
140.
141.
142. //恢复所有变量
143. bMouseDown = false;
144. bDragging = false;
145. bMouseDownArrowLeft = false;
146. bMouseDownArrowRight = false;
147.
148. CStatic::OnLButtonUp(nFlags, point);
149.}
150.
151.void CSkinHorizontalScrollbar::OnMouseMove(UINT nFlags, CPoint point)
152.{
153. CRect clientRect;
154. GetClientRect(&clientRect);
155.
156. if(bMouseDown)
157. bDragging = true;
158.
159. if(bDragging)
160. {
161. //滑块宽度 26 , 此处假定鼠标点中的是滑块的中点, 计算滑块位置
162. nThumbLeft = point.x-13;
163.
164. double nMax = pList->GetScrollLimit(SB_HORZ);
165. int nPos = pList->GetScrollPos(SB_HORZ);
166.
167. // 计算可滚动的范围: clientRect.Width() - 左箭头宽 - 右箭头宽 - 滑块宽
168. double nWidth = clientRect.Width()-75;
169. double nVar = nMax;
170. dbThumbInterval = nWidth/nVar;
171.
172. // nThumbLeft-25: 滑块偏离起点的距离
173. // (nThumbLeft-25)/dbThumbInterval 需要滚动的次数
174. int nScrollTimes = (int)((nThumbLeft-25)/dbThumbInterval)-nPos;
175.
176. CSize size;
177. size.cx = nScrollTimes;
178. size.cy = 0;
179.
180. // 强制列表滚动
181. pList->Scroll(size);
182.
183. LimitThumbPosition();
184.
185. Draw();
186. }
187.
188. CStatic::OnMouseMove(nFlags, point);
189.}
190.
191.void CSkinHorizontalScrollbar::OnTimer(UINT nIDEvent)
192.{
193. if(nIDEvent == 1)
194. {
195. if(bMouseDownArrowRight)
196. {
197. ScrollRight();
198. }
199.
200. if(bMouseDownArrowLeft)
201. {
202. ScrollLeft();
203. }
204. }
205. else if(nIDEvent == 2)
206. {
207. if(bMouseDownArrowRight)
208. {
209. KillTimer(2);
210. SetTimer(1, 50, NULL);
211. }
212.
213. if(bMouseDownArrowLeft)
214. {
215. KillTimer(2);
216. SetTimer(1, 50, NULL);
217. }
218. }
219.
220. CStatic::OnTimer(nIDEvent);
221.}
222.
223.void CSkinHorizontalScrollbar::ScrollLeft()
224.{
225. pList->SendMessage(WM_HSCROLL, MAKELONG(SB_LINELEFT,0),NULL);
226. UpdateThumbPosition();
227.}
228.
229.void CSkinHorizontalScrollbar::ScrollRight()
230.{
231. pList->SendMessage(WM_HSCROLL, MAKELONG(SB_LINERIGHT,0),NULL);
232. UpdateThumbPosition();
233.}
234.
235.void CSkinHorizontalScrollbar::UpdateThumbPosition()
236.{
237. CRect clientRect;
238. GetClientRect(&clientRect);
239.
240. double nPos = pList->GetScrollPos(SB_HORZ);
241. double nMax = pList->GetScrollLimit(SB_HORZ);
242. double nWidth = clientRect.Width()-75;
243. double nVar = nMax;
244.
245. dbThumbInterval = nWidth/nVar;
246.
247. double nNewdbValue = dbThumbInterval * (nPos);
248. int nNewValue = (int)nNewdbValue;
249. //double nExtra = nNewdbValue - nNewValue;
250. //dbThumbRemainder = nExtra;
251.
252. nThumbLeft = 25+nNewValue;
253.
254. TRACE("nPos: %0.3f, nMax : %0.3f, nWidth: %0.3f, dbThumbInterval: %0.3f\r\n",
255. nPos , nMax, nWidth, dbThumbInterval);
256.
257. LimitThumbPosition();
258.
259. Draw();
260.}
261.
262.void CSkinHorizontalScrollbar::PageRight()
263.{
264. TRACE("CSkinHorizontalScrollbar::PageRight \r\n");
265. pList->SendMessage(WM_HSCROLL, MAKELONG(SB_PAGEDOWN,0),NULL);
266. UpdateThumbPosition();
267.}
268.
269.void CSkinHorizontalScrollbar::PageLeft()
270.{
271. TRACE("CSkinHorizontalScrollbar::PageLeft \r\n");
272. pList->SendMessage(WM_HSCROLL, MAKELONG(SB_PAGEUP,0),NULL);
273. UpdateThumbPosition();
274.}
275.
276.void CSkinHorizontalScrollbar::Draw()
277.{
278. CClientDC dc(this);
279. CRect clientRect;
280. GetClientRect(&clientRect);
281. CMemDC memDC(&dc, &clientRect);
282.
283. // 填充背景
284. memDC.FillSolidRect(&clientRect, RGB(76,85,118));
285.
286. //画左箭头
287. CDC bitmapDC;
288. bitmapDC.CreateCompatibleDC(&dc);
289. CBitmap bitmap;
290. bitmap.LoadBitmap(IDB_HORIZONTAL_SCROLLBAR_LEFTARROW);
291. CBitmap* pOldBitmap = bitmapDC.SelectObject(&bitmap);
292. memDC.BitBlt(clientRect.left,clientRect.top,26,12,&bitmapDC,0,0,SRCCOPY);
293.
294. bitmapDC.SelectObject(pOldBitmap);
295. bitmap.DeleteObject();
296. pOldBitmap = NULL;
297.
298.
299. //画中间背景
300. bitmap.LoadBitmap(IDB_HORIZONTAL_SCROLLBAR_SPAN);
301. pOldBitmap = bitmapDC.SelectObject(&bitmap);
302. int nWidth = clientRect.Width() - 26;
303. memDC.StretchBlt(clientRect.left+26, clientRect.top, nWidth,12,&bitmapDC, 0,0, 1, 12, SRCCOPY);
304. bitmapDC.SelectObject(pOldBitmap);
305. bitmap.DeleteObject();
306. pOldBitmap = NULL;
307.
308.
309. //画右箭头
310. bitmap.LoadBitmap(IDB_HORIZONTAL_SCROLLBAR_RIGHTARROW);
311. pOldBitmap = bitmapDC.SelectObject(&bitmap);
312. memDC.BitBlt(nWidth,clientRect.top,26,12,&bitmapDC,0,0,SRCCOPY);
313. bitmapDC.SelectObject(pOldBitmap);
314. bitmap.DeleteObject();
315. pOldBitmap = NULL;
316.
317.
318.
319. //If there is nothing to scroll then don't
320. //show the thumb control otherwise show it
321. TRACE("pList->GetScrollLimit: %d\r\n", pList->GetScrollLimit(SB_HORZ) );
322.
323. if(pList->GetScrollLimit(SB_HORZ) != 0)
324. {
325. bitmap.LoadBitmap(IDB_HORIZONTAL_SCROLLBAR_THUMB);
326.
327. pOldBitmap = bitmapDC.SelectObject(&bitmap);
328.
329. memDC.BitBlt( clientRect.left+nThumbLeft, clientRect.top, 26,12,
330. &bitmapDC, 0, 0, SRCCOPY);
331.
332. bitmapDC.SelectObject(pOldBitmap);
333. bitmap.DeleteObject();
334. pOldBitmap = NULL;
335. }
336.
337.
338.}
339.
340.void CSkinHorizontalScrollbar::LimitThumbPosition()
341.{
342. CRect clientRect;
343. GetClientRect(&clientRect);
344.
345.
346. // 滑块的范围是 左右箭头之间的区域, 左右箭头宽 26 像素
347. if(nThumbLeft+26 > (clientRect.Width()-26))
348. {
349. nThumbLeft = clientRect.Width()-51;
350. }
351.
352. if(nThumbLeft < (clientRect.left+25))
353. {
354. nThumbLeft = clientRect.left+25;
355. }
356.}
// 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
view plaincopy to clipboardprint?
1.#if !defined(AFX_SKINHORIZONTALSCROLLBAR_H__77B6A7DF_1670_44D6_AA66_28424AF219DB__INCLUDED_)
2.#define AFX_SKINHORIZONTALSCROLLBAR_H__77B6A7DF_1670_44D6_AA66_28424AF219DB__INCLUDED_
3.
4.#include "memdc.h"
5.
6.
7.class CSkinHorizontalScrollbar : public CStatic
8.{
9.public:
10. CSkinHorizontalScrollbar();
11. void ScrollLeft();
12. void ScrollRight();
13.
14. bool bMouseDownArrowRight, bMouseDownArrowLeft;
15. bool bDragging;
16. bool bMouseDown;
17.
18. int nThumbLeft;
19. double dbThumbRemainder;
20. double dbThumbInterval;
21.
22.// Attributes
23.public:
24.
25.// Operations
26.public:
27.
28.
29.public:
30. CListCtrl* pList;
31. void LimitThumbPosition();
32. void Draw();
33. void PageLeft();
34. void PageRight();
35. void UpdateThumbPosition();
36. virtual ~CSkinHorizontalScrollbar();
37.
38.
39.
40.protected:
41. afx_msg void OnPaint();
42. afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
43. afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
44. afx_msg void OnMouseMove(UINT nFlags, CPoint point);
45. afx_msg void OnTimer(UINT nIDEvent);
46.
47. DECLARE_MESSAGE_MAP()
48.};
49.
50.#endif
#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 动态创建滚动条控件, 并定位滚动条的位置。
要为CListCtrl 添加滚动条,大概思路如下:
(1) 使 CListCtrl 标准的滚动条不可用
(2) 手动定位 H 和 V 滚动条的位置( 不属于 ClistCtrl 的子控件 )
(3) 需要处理,
鼠标点击 左右箭头, 滚动一行
鼠标点击 滑块空白处, 滚动一页
鼠标拖拽滑块
下面是简单示例图: 滑块,左右箭头均是 宽26像素 高12像素
分析代码如下 SkinHorizontalScrollbar.cpp
view plaincopy to clipboardprint?
1.// SkinHorizontalScrollbar.cpp : implementation file
2.//
3.
4.#include "stdafx.h"
5.#include "test_scroll.h"
6.#include "SkinHorizontalScrollbar.h"
7.
8.#ifdef _DEBUG
9.#define new DEBUG_NEW
10.#undef THIS_FILE
11.static char THIS_FILE[] = __FILE__;
12.#endif
13.
14./
15.// CSkinHorizontalScrollbar
16.
17.CSkinHorizontalScrollbar::CSkinHorizontalScrollbar()
18.{
19. nThumbLeft = 25;
20. dbThumbRemainder = 0.00f;
21.
22. bMouseDown = false;
23. bMouseDownArrowLeft = false;
24. bMouseDownArrowRight = false;
25. bDragging = false;
26. pList = NULL;
27.}
28.
29.CSkinHorizontalScrollbar::~CSkinHorizontalScrollbar()
30.{
31.}
32.
33.
34.BEGIN_MESSAGE_MAP(CSkinHorizontalScrollbar, CStatic)
35. //{{AFX_MSG_MAP(CSkinHorizontalScrollbar)
36. ON_WM_PAINT()
37. ON_WM_LBUTTONDOWN()
38. ON_WM_LBUTTONUP()
39. ON_WM_MOUSEMOVE()
40. ON_WM_TIMER()
41. //}}AFX_MSG_MAP
42.END_MESSAGE_MAP()
43.
44./
45.// CSkinHorizontalScrollbar message handlers
46.
47.void CSkinHorizontalScrollbar::OnPaint()
48.{
49. CPaintDC dc(this); // device context for painting
50.
51. Draw();
52.}
53.
54.void CSkinHorizontalScrollbar::OnLButtonDown(UINT nFlags, CPoint point)
55.{
56. SetCapture();
57. CRect clientRect;
58. GetClientRect(&clientRect);
59.
60. int nWidth = clientRect.Width()-26;
61.
62. // 左箭头的区域
63. CRect rectLeftArrow(0,0,26,20);
64. // 右箭头区域
65. CRect rectRightArrow(nWidth,0,nWidth+26,20);
66. // 滑块区域
67. CRect rectThumb(nThumbLeft,0,nThumbLeft+26,20);
68.
69.
70. // 是否点重滑块
71. if(rectThumb.PtInRect(point))
72. {
73. bMouseDown = true;
74. }
75.
76. // 是否点重右箭头
77. if(rectRightArrow.PtInRect(point))
78. {
79. bMouseDownArrowRight = true;
80. SetTimer(2,250,NULL);
81. }
82. // 是否点重左箭头
83. if(rectLeftArrow.PtInRect(point))
84. {
85. bMouseDownArrowLeft = true;
86. SetTimer(2,250,NULL);
87. }
88.
89. CStatic::OnLButtonDown(nFlags, point);
90.}
91.
92.void CSkinHorizontalScrollbar::OnLButtonUp(UINT nFlags, CPoint point)
93.{
94. UpdateThumbPosition();
95. KillTimer(1);
96. ReleaseCapture();
97.
98.
99. bool bInChannel = true;
100.
101. CRect clientRect;
102. GetClientRect(&clientRect);
103.
104. int nWidth = clientRect.Width()-26;
105.
106. CRect rectLeftArrow(0,0,26,20);
107. CRect rectThumb(nThumbLeft,0,nThumbLeft+26,20);
108.
109. if(rectLeftArrow.PtInRect(point))
110. {
111. ScrollLeft();
112. bInChannel = false;
113. }
114.
115. CRect rectRightArrow(nWidth,0,nWidth+26,20);
116.
117.
118. if(rectRightArrow.PtInRect(point))
119. {
120. ScrollRight();
121. bInChannel = false;
122. }
123.
124. if(rectThumb.PtInRect(point))
125. {
126. bInChannel = false;
127. }
128.
129. if(bInChannel == true && !bMouseDown)
130. {
131. if(point.x > nThumbLeft)
132. {
133. PageRight();
134. }
135. else
136. {
137. PageLeft();
138. }
139. }
140.
141.
142. //恢复所有变量
143. bMouseDown = false;
144. bDragging = false;
145. bMouseDownArrowLeft = false;
146. bMouseDownArrowRight = false;
147.
148. CStatic::OnLButtonUp(nFlags, point);
149.}
150.
151.void CSkinHorizontalScrollbar::OnMouseMove(UINT nFlags, CPoint point)
152.{
153. CRect clientRect;
154. GetClientRect(&clientRect);
155.
156. if(bMouseDown)
157. bDragging = true;
158.
159. if(bDragging)
160. {
161. //滑块宽度 26 , 此处假定鼠标点中的是滑块的中点, 计算滑块位置
162. nThumbLeft = point.x-13;
163.
164. double nMax = pList->GetScrollLimit(SB_HORZ);
165. int nPos = pList->GetScrollPos(SB_HORZ);
166.
167. // 计算可滚动的范围: clientRect.Width() - 左箭头宽 - 右箭头宽 - 滑块宽
168. double nWidth = clientRect.Width()-75;
169. double nVar = nMax;
170. dbThumbInterval = nWidth/nVar;
171.
172. // nThumbLeft-25: 滑块偏离起点的距离
173. // (nThumbLeft-25)/dbThumbInterval 需要滚动的次数
174. int nScrollTimes = (int)((nThumbLeft-25)/dbThumbInterval)-nPos;
175.
176. CSize size;
177. size.cx = nScrollTimes;
178. size.cy = 0;
179.
180. // 强制列表滚动
181. pList->Scroll(size);
182.
183. LimitThumbPosition();
184.
185. Draw();
186. }
187.
188. CStatic::OnMouseMove(nFlags, point);
189.}
190.
191.void CSkinHorizontalScrollbar::OnTimer(UINT nIDEvent)
192.{
193. if(nIDEvent == 1)
194. {
195. if(bMouseDownArrowRight)
196. {
197. ScrollRight();
198. }
199.
200. if(bMouseDownArrowLeft)
201. {
202. ScrollLeft();
203. }
204. }
205. else if(nIDEvent == 2)
206. {
207. if(bMouseDownArrowRight)
208. {
209. KillTimer(2);
210. SetTimer(1, 50, NULL);
211. }
212.
213. if(bMouseDownArrowLeft)
214. {
215. KillTimer(2);
216. SetTimer(1, 50, NULL);
217. }
218. }
219.
220. CStatic::OnTimer(nIDEvent);
221.}
222.
223.void CSkinHorizontalScrollbar::ScrollLeft()
224.{
225. pList->SendMessage(WM_HSCROLL, MAKELONG(SB_LINELEFT,0),NULL);
226. UpdateThumbPosition();
227.}
228.
229.void CSkinHorizontalScrollbar::ScrollRight()
230.{
231. pList->SendMessage(WM_HSCROLL, MAKELONG(SB_LINERIGHT,0),NULL);
232. UpdateThumbPosition();
233.}
234.
235.void CSkinHorizontalScrollbar::UpdateThumbPosition()
236.{
237. CRect clientRect;
238. GetClientRect(&clientRect);
239.
240. double nPos = pList->GetScrollPos(SB_HORZ);
241. double nMax = pList->GetScrollLimit(SB_HORZ);
242. double nWidth = clientRect.Width()-75;
243. double nVar = nMax;
244.
245. dbThumbInterval = nWidth/nVar;
246.
247. double nNewdbValue = dbThumbInterval * (nPos);
248. int nNewValue = (int)nNewdbValue;
249. //double nExtra = nNewdbValue - nNewValue;
250. //dbThumbRemainder = nExtra;
251.
252. nThumbLeft = 25+nNewValue;
253.
254. TRACE("nPos: %0.3f, nMax : %0.3f, nWidth: %0.3f, dbThumbInterval: %0.3f\r\n",
255. nPos , nMax, nWidth, dbThumbInterval);
256.
257. LimitThumbPosition();
258.
259. Draw();
260.}
261.
262.void CSkinHorizontalScrollbar::PageRight()
263.{
264. TRACE("CSkinHorizontalScrollbar::PageRight \r\n");
265. pList->SendMessage(WM_HSCROLL, MAKELONG(SB_PAGEDOWN,0),NULL);
266. UpdateThumbPosition();
267.}
268.
269.void CSkinHorizontalScrollbar::PageLeft()
270.{
271. TRACE("CSkinHorizontalScrollbar::PageLeft \r\n");
272. pList->SendMessage(WM_HSCROLL, MAKELONG(SB_PAGEUP,0),NULL);
273. UpdateThumbPosition();
274.}
275.
276.void CSkinHorizontalScrollbar::Draw()
277.{
278. CClientDC dc(this);
279. CRect clientRect;
280. GetClientRect(&clientRect);
281. CMemDC memDC(&dc, &clientRect);
282.
283. // 填充背景
284. memDC.FillSolidRect(&clientRect, RGB(76,85,118));
285.
286. //画左箭头
287. CDC bitmapDC;
288. bitmapDC.CreateCompatibleDC(&dc);
289. CBitmap bitmap;
290. bitmap.LoadBitmap(IDB_HORIZONTAL_SCROLLBAR_LEFTARROW);
291. CBitmap* pOldBitmap = bitmapDC.SelectObject(&bitmap);
292. memDC.BitBlt(clientRect.left,clientRect.top,26,12,&bitmapDC,0,0,SRCCOPY);
293.
294. bitmapDC.SelectObject(pOldBitmap);
295. bitmap.DeleteObject();
296. pOldBitmap = NULL;
297.
298.
299. //画中间背景
300. bitmap.LoadBitmap(IDB_HORIZONTAL_SCROLLBAR_SPAN);
301. pOldBitmap = bitmapDC.SelectObject(&bitmap);
302. int nWidth = clientRect.Width() - 26;
303. memDC.StretchBlt(clientRect.left+26, clientRect.top, nWidth,12,&bitmapDC, 0,0, 1, 12, SRCCOPY);
304. bitmapDC.SelectObject(pOldBitmap);
305. bitmap.DeleteObject();
306. pOldBitmap = NULL;
307.
308.
309. //画右箭头
310. bitmap.LoadBitmap(IDB_HORIZONTAL_SCROLLBAR_RIGHTARROW);
311. pOldBitmap = bitmapDC.SelectObject(&bitmap);
312. memDC.BitBlt(nWidth,clientRect.top,26,12,&bitmapDC,0,0,SRCCOPY);
313. bitmapDC.SelectObject(pOldBitmap);
314. bitmap.DeleteObject();
315. pOldBitmap = NULL;
316.
317.
318.
319. //If there is nothing to scroll then don't
320. //show the thumb control otherwise show it
321. TRACE("pList->GetScrollLimit: %d\r\n", pList->GetScrollLimit(SB_HORZ) );
322.
323. if(pList->GetScrollLimit(SB_HORZ) != 0)
324. {
325. bitmap.LoadBitmap(IDB_HORIZONTAL_SCROLLBAR_THUMB);
326.
327. pOldBitmap = bitmapDC.SelectObject(&bitmap);
328.
329. memDC.BitBlt( clientRect.left+nThumbLeft, clientRect.top, 26,12,
330. &bitmapDC, 0, 0, SRCCOPY);
331.
332. bitmapDC.SelectObject(pOldBitmap);
333. bitmap.DeleteObject();
334. pOldBitmap = NULL;
335. }
336.
337.
338.}
339.
340.void CSkinHorizontalScrollbar::LimitThumbPosition()
341.{
342. CRect clientRect;
343. GetClientRect(&clientRect);
344.
345.
346. // 滑块的范围是 左右箭头之间的区域, 左右箭头宽 26 像素
347. if(nThumbLeft+26 > (clientRect.Width()-26))
348. {
349. nThumbLeft = clientRect.Width()-51;
350. }
351.
352. if(nThumbLeft < (clientRect.left+25))
353. {
354. nThumbLeft = clientRect.left+25;
355. }
356.}
// 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
view plaincopy to clipboardprint?
1.#if !defined(AFX_SKINHORIZONTALSCROLLBAR_H__77B6A7DF_1670_44D6_AA66_28424AF219DB__INCLUDED_)
2.#define AFX_SKINHORIZONTALSCROLLBAR_H__77B6A7DF_1670_44D6_AA66_28424AF219DB__INCLUDED_
3.
4.#include "memdc.h"
5.
6.
7.class CSkinHorizontalScrollbar : public CStatic
8.{
9.public:
10. CSkinHorizontalScrollbar();
11. void ScrollLeft();
12. void ScrollRight();
13.
14. bool bMouseDownArrowRight, bMouseDownArrowLeft;
15. bool bDragging;
16. bool bMouseDown;
17.
18. int nThumbLeft;
19. double dbThumbRemainder;
20. double dbThumbInterval;
21.
22.// Attributes
23.public:
24.
25.// Operations
26.public:
27.
28.
29.public:
30. CListCtrl* pList;
31. void LimitThumbPosition();
32. void Draw();
33. void PageLeft();
34. void PageRight();
35. void UpdateThumbPosition();
36. virtual ~CSkinHorizontalScrollbar();
37.
38.
39.
40.protected:
41. afx_msg void OnPaint();
42. afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
43. afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
44. afx_msg void OnMouseMove(UINT nFlags, CPoint point);
45. afx_msg void OnTimer(UINT nIDEvent);
46.
47. DECLARE_MESSAGE_MAP()
48.};
49.
50.#endif
#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 即可完成。