我自己做的自定义滚动条,练练手。
自定义一个CCustomScroll类,从CWnd中派生
头文件(h)源码
class CCustomScroll : public CWnd
{ // Construction public: CCustomScroll(); UINT m_ThumbWidth; //滚动块和箭头宽度 UINT m_ThumbHeight; //滚动块和箭头高度 CWnd* m_pParent; //父窗口 CRect m_ClientRect; //窗口客户区域 CRect m_ThumbRect; //滚动块区域
BOOL m_ButtonDown; //鼠标是否单击滚动块
CPoint m_Startpt; //鼠标按下时的起点 BOOL m_IsLeft; //滚动块是否超过左箭头 BOOL m_IsLeftArrow; //是否单击左滚动条按钮 BOOL m_IsRightArrow;//是否单击右滚动条按钮 BOOL m_IsLeftRange; //是否单击了左滚动区域 BOOL m_IsRightRange;//是否单击了右滚动区域 UINT m_MinRange; //最小滚动范围
UINT m_MaxRange; //最大滚动范围 UINT m_CurPos; //当前的位置(逻辑单位) double m_Rate; //物理像素与逻辑单位的比率 UINT m_LeftArrow; //左箭头位图ID
UINT m_RightArrow; //右箭头位图ID UINT m_ChanelBK; //背景位图ID UINT m_ThumbBK; //滚动块位图ID // Attributes
public: // Operations
public: // Overrides
// ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CCustomScroll) //}}AFX_VIRTUAL // Implementation
public: void SetScrollRange(int minRange,int maxRange); void DrawHorScroll(); void DrawControl(); virtual ~CCustomScroll(); // Generated message map functions
protected: //{{AFX_MSG(CCustomScroll) 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); //}}AFX_MSG DECLARE_MESSAGE_MAP()
}; 执行文件(cpp)源
为了防止画面闪烁用了双缓冲绘图技术(就是在内存设备上下文里画图,然后用StretchBlt往屏幕设备上下文上贴图防止画面闪烁图技术),具体定义可以上网去查,这里不做太多说明。我再次写了一个CMemDC 类。
class CMemDC : public CDC
{ private: CBitmap* m_bmp; CBitmap* m_oldbmp; CDC* m_pDC; CRect m_Rect; public: CMemDC(CDC* pDC, const CRect& rect) : CDC() { CreateCompatibleDC(pDC); m_bmp = new CBitmap; m_bmp->CreateCompatibleBitmap(pDC, rect.Width(), rect.Height()); m_oldbmp = SelectObject(m_bmp); m_pDC = pDC; m_Rect = rect; } ~CMemDC() { m_pDC->BitBlt(m_Rect.left, m_Rect.top, m_Rect.Width(), m_Rect.Height(), this, m_Rect.left, m_Rect.top, SRCCOPY); SelectObject(m_oldbmp); if (m_bmp != NULL) delete m_bmp; } }; CCustomScroll::CCustomScroll()
{ m_ButtonDown = FALSE;//鼠标是否单击滚动块 m_IsLeft = FALSE;//滚动块是否超过左箭头 m_MinRange = 0; m_MaxRange = 200; m_CurPos = 0; m_IsLeftArrow = FALSE; m_IsRightArrow = FALSE; m_IsLeftRange = FALSE; m_IsRightRange = FALSE; } CCustomScroll::~CCustomScroll()
{ } BEGIN_MESSAGE_MAP(CCustomScroll, CStatic) //{{AFX_MSG_MAP(CCustomScroll) ON_WM_PAINT() ON_WM_LBUTTONDOWN() ON_WM_LBUTTONUP() ON_WM_MOUSEMOVE() ON_WM_TIMER() //}}AFX_MSG_MAP END_MESSAGE_MAP() 响应WM_PAINT消息,自制滚动条的函数就在这里了
void CCustomScroll::OnPaint()
{ CPaintDC dc(this); // device context for painting DrawControl(); // Do not call CStatic::OnPaint() for painting messages } void CCustomScroll::DrawControl()
{ DrawHorScroll(); //画水平滚动条 // DrawVerScroll(); 这里可以画垂直滚动条,原理一样,就不在此写了
} void CCustomScroll::DrawHorScroll()
{ //其实就是在内存设备上下文里画图,然后用StretchBlt往屏幕设备上下文上贴图
CClientDC dc(this); CMemDC memdc(&dc,m_ClientRect);
CDC bmpdc; bmpdc.CreateCompatibleDC(&dc); //当前匹配的CDC CBitmap bmp;
bmp.LoadBitmap(m_LeftArrow);//左箭头的图片 CBitmap* pOldbmp = bmpdc.SelectObject(&bmp); // 计算左箭头的位置
CRect LeftArrowRect (m_ClientRect.left,m_ClientRect.top,m_ClientRect.left+m_ThumbWidth,m_ClientRect.bottom); memdc.StretchBlt(m_ClientRect.left,m_ClientRect.top,m_ThumbWidth,m_ThumbHeight,&bmpdc,0,0,m_ThumbWidth,m_ThumbHeight,SRCCOPY); if (pOldbmp)
bmpdc.SelectObject(pOldbmp); if (bmp.GetSafeHandle()) bmp.DeleteObject(); pOldbmp = NULL;
//通道的开始位置和宽度 int nChanelStart = m_ClientRect.left+m_ThumbWidth; int nChanelWidth = m_ClientRect.Width()- 2*m_ThumbWidth; //绘制通道 bmp.LoadBitmap(m_ChanelBK); pOldbmp = bmpdc.SelectObject(&bmp);
memdc.StretchBlt(nChanelStart,m_ClientRect.top,nChanelWidth,m_ClientRect.Height(),&bmpdc,0,0,1,10,SRCCOPY);
if (pOldbmp) bmpdc.SelectObject(pOldbmp); if (bmp.GetSafeHandle()) bmp.DeleteObject(); //绘制右箭头
bmp.LoadBitmap(m_RightArrow); pOldbmp = bmpdc.SelectObject(&bmp); int nRArrowStart = m_ThumbWidth+nChanelWidth;
memdc.StretchBlt(nRArrowStart,m_ClientRect.top,m_ThumbWidth,m_ClientRect.Height(),&bmpdc,0,0,m_ThumbWidth,m_ThumbHeight,SRCCOPY); //绘制滚动块 if (bmp.GetSafeHandle()) bmp.DeleteObject(); bmp.LoadBitmap(m_ThumbBK);
pOldbmp = bmpdc.SelectObject(&bmp); memdc.StretchBlt(m_ThumbRect.left,m_ThumbRect.top,m_ThumbRect.Width()+1,m_ThumbRect.Height(),&bmpdc,0,0,m_ThumbRect.Width(),m_ThumbRect.Height(),SRCCOPY); }
下面这个函数是处理鼠标按下事件的,如果按下的是向上或者向下按钮,并且按住不放,那么Thumb必须连续移动,但是MFC是不处理按下不放的事件的,所以这里得设置一个定制器。还有个问题就是如果鼠标拖动Thumb,在拖动过程中鼠标移出了CScrollBarEx区域,MFC也不会处理鼠标移动事件的,要用SetCapture();来捕捉消息。注意要在鼠标谈起的时候释放捕捉ReleaseCapture()
void CCustomScroll::OnLButtonDown(UINT nFlags, CPoint point)
{ m_Startpt = point; //确定滚动区域
CRect rcScroll = m_ClientRect; rcScroll.left += m_ThumbWidth;
rcScroll.right-= m_ThumbWidth; SetCapture();
DWORD wparam;
if (m_ThumbRect.PtInRect(point))//判断是否点在滑动块上 { m_ButtonDown = TRUE; } else if (rcScroll.PtInRect(point)) //单击滚动区域
{
CPoint centerPt = m_ThumbRect.CenterPoint();
int offset = point.x-centerPt.x; if ((int)point.x<m_ThumbRect.left) //是否点击到左滚动区域 m_IsLeftRange = TRUE; if ((int)point.x>m_ThumbRect.right)//是否点击到右滚动区域 m_IsRightRange= TRUE; m_ThumbRect.OffsetRect(offset,0);滑动块移动
if (m_ThumbRect.left<(int)m_ThumbWidth) //判断当前滚动量是否超出了左滚动范围
{
int width = m_ThumbRect.Width();
m_ThumbRect.left = m_ThumbWidth; m_ThumbRect.right = m_ThumbRect.left+width; m_CurPos = m_MinRange; wparam = MAKELONG(SB_PAGELEFT,m_CurPos) ;
::SendMessage(GetParent()->m_hWnd,WM_HSCROLL,wparam,(LPARAM)m_hWnd);
DrawControl();
return; }
else if (m_ThumbRect.right > right>(int)(m_ClientRect.Width()-m_ThumbWidth))//是否超出了右滚动范围
{
int width = m_ThumbRect.Width();
m_ThumbRect.left = m_ThumbWidth; m_ThumbRect.right = m_ThumbRect.left+width; m_CurPos = m_MinRange; wparam = MAKELONG(SB_PAGELEFT,m_CurPos) ;
::SendMessage(GetParent()->m_hWnd,WM_HSCROLL,wparam,(LPARAM)m_hWnd);
DrawControl();
return; }
else
{
int range = m_ThumbRect.left-m_ThumbWidth;
m_CurPos = m_Rate*(range); if (m_IsLeftRange)
{ wparam = MAKELONG(SB_PAGELEFT,m_CurPos) ; ::SendMessage(GetParent()->m_hWnd,WM_HSCROLL,wparam,(LPARAM)m_hWnd); } else if (m_IsRightRange) { wparam = MAKELONG(SB_PAGERIGHT,m_CurPos); ::SendMessage(GetParent()->m_hWnd,WM_HSCROLL,wparam,(LPARAM)m_hWnd); } DrawControl(); return;
}
}
else //单击箭头按钮
{ if (point.x<=(int)m_ThumbWidth) //单击左箭头
{ if (m_CurPos>m_MinRange) wparam = MAKELONG(SB_LINELEFT ,1); else wparam = MAKELONG(SB_LINELEFT ,0); ::SendMessage(GetParent()->m_hWnd,WM_HSCROLL,wparam,(LPARAM)m_hWnd); if (m_CurPos>m_MinRange) m_CurPos-=1; m_IsLeftArrow = TRUE; } else //单击右箭头 { if (m_CurPos>=m_MaxRange) wparam = MAKELONG(SB_LINERIGHT ,0); else wparam = MAKELONG(SB_LINERIGHT ,1); ::SendMessage(GetParent()->m_hWnd,WM_HSCROLL,wparam,(LPARAM)m_hWnd); if (m_CurPos<m_MaxRange) m_CurPos+=1; m_IsRightArrow = TRUE; }
int factpos = m_CurPos/m_Rate; int width = m_ThumbRect.Width(); m_ThumbRect.left = m_ThumbWidth +factpos; m_ThumbRect.right = m_ThumbRect.left+width; DrawControl();
SetTimer(1,100,NULL); } CWnd::OnLButtonDown(nFlags, point); }
//定时器可以使一直按着左右键来控制滑动块变成可能
void CCustomScroll::OnTimer(UINT nIDEvent)
{ DWORD wparam; if (m_IsLeftArrow) { if (m_CurPos>m_MinRange) wparam = MAKELONG(SB_LINELEFT ,1); else wparam = MAKELONG(SB_LINELEFT ,0); ::SendMessage(GetParent()->m_hWnd,WM_HSCROLL,wparam,(LPARAM)m_hWnd); if (m_CurPos>m_MinRange) m_CurPos-=1; } else if (m_IsRightArrow) { if (m_CurPos< m_MaxRange) wparam = MAKELONG(SB_LINERIGHT,1); else wparam = MAKELONG(SB_LINERIGHT ,0); ::SendMessage(GetParent()->m_hWnd,WM_HSCROLL,wparam,(LPARAM)m_hWnd); if (m_CurPos<m_MaxRange) m_CurPos+=1; } int factpos = m_CurPos/m_Rate; int width = m_ThumbRect.Width(); m_ThumbRect.left = m_ThumbWidth +factpos; m_ThumbRect.right = m_ThumbRect.left+width; DrawControl(); CWnd::OnTimer(nIDEvent);
} void CCustomScroll::OnMouseMove(UINT nFlags, CPoint point)
{ if (m_ButtonDown) { int offset = point.x-m_Startpt.x; m_Startpt = point; DWORD wparam; if (offset<=0) //向左拖动滚动块 { if (m_ThumbRect.left<=(int)m_ThumbWidth) return; else if (abs(offset)>(int)(m_ThumbRect.left-m_ThumbWidth)) //判断当前滚动量是否超出了滚动范围 { int width = m_ThumbRect.Width(); m_ThumbRect.left = m_ThumbWidth; m_ThumbRect.right = m_ThumbRect.left+width; m_CurPos = 0; wparam = MAKELONG(SB_THUMBPOSITION,m_CurPos); ::SendMessage(GetParent()->m_hWnd,WM_HSCROLL,wparam,(LPARAM)m_hWnd); DrawControl(); return; } } else if (offset>0) //向右拖动滚动块 { if (m_ThumbRect.right>=m_ClientRect.Width()-m_ThumbWidth) //超出右箭头 { return; } else if ( offset> m_ClientRect.Width()-m_ThumbWidth-m_ThumbRect.right) //判断是否超出了滚动范围 { int width = m_ThumbRect.Width(); m_ThumbRect.right = m_ClientRect.Width()-m_ThumbWidth; m_ThumbRect.left = m_ThumbRect.right -width; m_CurPos = m_MaxRange; wparam = MAKELONG(SB_THUMBPOSITION,m_CurPos); ::SendMessage(GetParent()->m_hWnd,WM_HSCROLL,wparam,(LPARAM)m_hWnd); DrawControl(); return; } } m_ThumbRect.OffsetRect(offset,0);
int range = m_ThumbRect.left-m_ThumbWidth; m_CurPos = m_Rate*(range); wparam = MAKELONG(SB_THUMBPOSITION,m_CurPos);
::SendMessage(GetParent()->m_hWnd,WM_HSCROLL,wparam,(LPARAM)m_hWnd); DrawHorScroll();
} CWnd::OnMouseMove(nFlags, point); } void CCustomScroll::OnLButtonUp(UINT nFlags, CPoint point)
{ ReleaseCapture(); m_ButtonDown = FALSE; m_IsLeftRange = FALSE; m_IsRightRange = FALSE; if (m_IsLeftArrow)
{ m_IsLeftArrow = FALSE; KillTimer(1); } if (m_IsRightArrow) { m_IsRightArrow = FALSE; KillTimer(1); } CWnd::OnLButtonUp(nFlags, point);
} 写到这里,自定义的CCustomScroll类基本写好了。我在VC2003下试了,没有问题,可以使用。
|
|