我自己做的自定義滾動(dòng)條,練練手。
自定義一個(gè)CCustomScroll類,從CWnd中派生
頭文件(h)源碼
class CCustomScroll : public CWnd
{
// Construction
public:
CCustomScroll();
UINT m_ThumbWidth; //滾動(dòng)塊和箭頭寬度
UINT m_ThumbHeight; //滾動(dòng)塊和箭頭高度
CWnd* m_pParent; //父窗口
CRect m_ClientRect; //窗口客戶區(qū)域
CRect m_ThumbRect; //滾動(dòng)塊區(qū)域
BOOL m_ButtonDown; //鼠標(biāo)是否單擊滾動(dòng)塊
CPoint m_Startpt; //鼠標(biāo)按下時(shí)的起點(diǎn)
BOOL m_IsLeft; //滾動(dòng)塊是否超過左箭頭
BOOL m_IsLeftArrow; //是否單擊左滾動(dòng)條按鈕
BOOL m_IsRightArrow;//是否單擊右滾動(dòng)條按鈕
BOOL m_IsLeftRange; //是否單擊了左滾動(dòng)區(qū)域
BOOL m_IsRightRange;//是否單擊了右滾動(dòng)區(qū)域
UINT m_MinRange; //最小滾動(dòng)范圍
UINT m_MaxRange; //最大滾動(dòng)范圍
UINT m_CurPos; //當(dāng)前的位置(邏輯單位)
double m_Rate; //物理像素與邏輯單位的比率
UINT m_LeftArrow; //左箭頭位圖ID
UINT m_RightArrow; //右箭頭位圖ID
UINT m_ChanelBK; //背景位圖ID
UINT m_ThumbBK; //滾動(dòng)塊位圖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()
};
執(zhí)行文件(cpp)源
為了防止畫面閃爍用了雙緩沖繪圖技術(shù)(就是在內(nèi)存設(shè)備上下文里畫圖,然后用StretchBlt往屏幕設(shè)備上下文上貼圖防止畫面閃爍圖技術(shù)),具體定義可以上網(wǎng)去查,這里不做太多說明。我再次寫了一個(gè)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;//鼠標(biāo)是否單擊滾動(dòng)塊
m_IsLeft = FALSE;//滾動(dòng)塊是否超過左箭頭
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()
響應(yīng)WM_PAINT消息,自制滾動(dòng)條的函數(shù)就在這里了
void CCustomScroll::OnPaint()
{
CPaintDC dc(this); // device context for painting
DrawControl();
// Do not call CStatic::OnPaint() for painting messages
}
void CCustomScroll::DrawControl()
{
DrawHorScroll(); //畫水平滾動(dòng)條
// DrawVerScroll(); 這里可以畫垂直滾動(dòng)條,原理一樣,就不在此寫了
}
void CCustomScroll::DrawHorScroll()
{
//其實(shí)就是在內(nèi)存設(shè)備上下文里畫圖,然后用StretchBlt往屏幕設(shè)備上下文上貼圖
CClientDC dc(this);
CMemDC memdc(&dc,m_ClientRect);
CDC bmpdc;
bmpdc.CreateCompatibleDC(&dc); //當(dāng)前匹配的CDC
CBitmap bmp;
bmp.LoadBitmap(m_LeftArrow);//左箭頭的圖片
CBitmap* pOldbmp = bmpdc.SelectObject(&bmp);
// 計(jì)算左箭頭的位置
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);
//繪制滾動(dòng)塊
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);
}
下面這個(gè)函數(shù)是處理鼠標(biāo)按下事件的,如果按下的是向上或者向下按鈕,并且按住不放,那么Thumb必須連續(xù)移動(dòng),但是MFC是不處理按下不放的事件的,所以這里得設(shè)置一個(gè)定制器。還有個(gè)問題就是如果鼠標(biāo)拖動(dòng)Thumb,在拖動(dòng)過程中鼠標(biāo)移出了CScrollBarEx區(qū)域,MFC也不會(huì)處理鼠標(biāo)移動(dòng)事件的,要用SetCapture();來捕捉消息。注意要在鼠標(biāo)談起的時(shí)候釋放捕捉ReleaseCapture()
void CCustomScroll::OnLButtonDown(UINT nFlags, CPoint point)
{
m_Startpt = point;
//確定滾動(dòng)區(qū)域
CRect rcScroll = m_ClientRect;
rcScroll.left += m_ThumbWidth;
rcScroll.right-= m_ThumbWidth;
SetCapture();
DWORD wparam;
if (m_ThumbRect.PtInRect(point))//判斷是否點(diǎn)在滑動(dòng)塊上
{
m_ButtonDown = TRUE;
}
else if (rcScroll.PtInRect(point)) //單擊滾動(dòng)區(qū)域
{
CPoint centerPt = m_ThumbRect.CenterPoint();
int offset = point.x-centerPt.x;
if ((int)point.x<m_ThumbRect.left) //是否點(diǎn)擊到左滾動(dòng)區(qū)域
m_IsLeftRange = TRUE;
if ((int)point.x>m_ThumbRect.right)//是否點(diǎn)擊到右滾動(dòng)區(qū)域
m_IsRightRange= TRUE;
m_ThumbRect.OffsetRect(offset,0);滑動(dòng)塊移動(dòng)
if (m_ThumbRect.left<(int)m_ThumbWidth) //判斷當(dāng)前滾動(dòng)量是否超出了左滾動(dòng)范圍
{
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))//是否超出了右滾動(dòng)范圍
{
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);
}
//定時(shí)器可以使一直按著左右鍵來控制滑動(dòng)塊變成可能
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) //向左拖動(dòng)滾動(dòng)塊
{
if (m_ThumbRect.left<=(int)m_ThumbWidth)
return;
else if (abs(offset)>(int)(m_ThumbRect.left-m_ThumbWidth)) //判斷當(dāng)前滾動(dòng)量是否超出了滾動(dòng)范圍
{
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) //向右拖動(dòng)滾動(dòng)塊
{
if (m_ThumbRect.right>=m_ClientRect.Width()-m_ThumbWidth) //超出右箭頭
{
return;
}
else if ( offset> m_ClientRect.Width()-m_ThumbWidth-m_ThumbRect.right) //判斷是否超出了滾動(dòng)范圍
{
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下試了,沒有問題,可以使用。