您的位置:首页 > 编程语言 > C语言/C++

VC++/MFC精讲多练#004:DIY一个漂亮的滚动条控件

2014-12-15 11:36 369 查看
http://hi.topsage.com/thread-2316243-1-1.html

滚动条处处可见,一次性展示不完整,要用它拖动查看全部,而且下载的软件,滚动条漂亮多样,本文就是一步步和大家一起制作一个比较漂亮的滚动条。

严格来说这个滚动条,不是Scrollbar,为了DIY一个漂亮的滚动条,这里我们用CStatic控件。

要实现自定义滚动条控件,主要有三种方法。

一是利用钩子技术重新绘制滚动条,该方法实现起来比较复杂。

二是获得滚动条的显示区域,将其扣除,然后在该区域显示自定义的滚动条控件。

三是自定义一个滚动条,将其与对话框中的某个控件关联,在创建滚动条控件时,将对话框中的某个控件隐藏,并在该控件的位置显示滚动条控件。

本文采用第3种方法。

利用CStatic控件派生一个自定义滚动条CCustomScroll,在CStatic控件上利用位图绘制滚动条箭头、滚动块及滚动条的滚动区域。在绘制滚动条时,由于滚动块能够被拖动,因此需要频繁绘制滚动条。为了防止出现屏幕闪烁,可以定义一个临时的CDC对象,将所有的绘图操作都在该临时对象上进行,然后再将临时对象的内容绘制在滚动块的显示区域。

为了简化操作,本文将临时CDC对象功能封装为 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;

}

};

1. 新建一个基于对话框的MFC应用程序,在对话框中添加按钮、编辑框、静态文本控件,向对话框类中添加成员变量:

class CCustomScroll : public CStatic

{

// 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

2. 在CCustomScroll类的构造函数中初始化成员变量,代码如下:

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;

}

3. 向CCustomScroll类中添加CreateStatic成员函数,用于创建滚动条控件,如是:

BOOL CCustomScroll::CreateStatic(CWnd *pParent, DWORD dwStyle, UINT nIDStatic, UINT nID)

{

m_pParent = pParent;

ASSERT(m_pParent);

//获取父窗口中的静态文本

ASSERT(::IsWindow(pParent->GetDlgItem(nIDStatic)->m_hWnd));

CRect recttemp;

pParent->GetDlgItem(nIDStatic)->GetWindowRect(recttemp);

pParent->ScreenToClient(&recttemp);

m_ClientRect = recttemp;

pParent->GetDlgItem(nIDStatic)->ShowWindow(SW_HIDE);

BOOL ret = CStatic::Create("",dwStyle,m_ClientRect,pParent,nID);

pParent->GetDlgItem(nIDStatic)->GetClientRect(m_ClientRect);

if (ret)

{

CBitmap bmp;

bmp.LoadBitmap(m_LeftArrow);

BITMAP bInfo;

bmp.GetBitmap(&bInfo);

m_ThumbHeight = bInfo.bmHeight;

m_ThumbWidth = bInfo.bmWidth;

if (bmp.GetSafeHandle())

bmp.DeleteObject();

bmp.LoadBitmap(IDB_THUMB);

bmp.GetBitmap(&bInfo);

m_ThumbRect.CopyRect(CRect(m_ThumbWidth,0,m_ThumbWidth+bInfo.bmWidth,bInfo.bmHeight));

if (bmp.GetSafeHandle())

bmp.DeleteObject();

SetScrollRange(m_MinRange,m_MaxRange);

}

ShowWindow(SW_SHOW);

return ret;

}

4. 向CCustomScroll类中添加DrawHorScroll方法,绘制滚动条:

void CCustomScroll::DrawHorScroll()

{

CClientDC dc(this);

CMemDC memdc(&dc,m_ClientRect);

CDC bmpdc;

bmpdc.CreateCompatibleDC(&dc);

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);

}

5. 处理CCustomScroll类的WM_LBUTTONDOWN消息,根据用户单击的不同区域移动滚动块,如下:

void CCustomScroll::OnLButtonDown(UINT nFlags, CPoint point)

{

m_Startpt = point;

//确定滚动区域

CRect rcScroll = m_ClientRect;

rcScroll.left += m_ThumbWidth;

rcScroll.right-= m_ThumbWidth;

DWORD wparam;

SetCapture();

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);

int left = m_ThumbRect.left;

int right = m_ThumbRect.right;

if (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 (right>(int)(m_ClientRect.Width()-m_ThumbWidth))

{

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_PAGERIGHT,m_CurPos);

::SendMessage(GetParent()->m_hWnd,WM_HSCROLL,wparam,(LPARAM)m_hWnd);

DrawControl();

return;

}

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();

}

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);

}

CStatic::OnLButtonDown(nFlags, point);

}

6. 处理CCustomScroll类的WM_MOUSEMOVE消息,如果用户正在拖动滚动条,将滚动块移动到适当的位置:

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();

}

CStatic::OnMouseMove(nFlags, point);

}

7. 处理对话框的WM_TIMER消息,当用户按下滚动条两端的箭头时,连续移动滚动块,代码如下:

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();

CStatic::OnTimer(nIDEvent);

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: