文章类型: VC&C++
关键词: VC++/MFC精讲多练#004:DIY一个漂亮的滚动条控件
内容摘要: VC++/MFC精讲多练#004:DIY一个漂亮的滚动条控件

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

2016/10/9 14:40:57    来源:apple    阅读:

滚动条处处可见,一次性展示不完整,要用它拖动查看全部,而且下载的软件,滚动条漂亮多样,本文就是一步步和大家一起制作一个比较漂亮的滚动条。
严格来说这个滚动条,不是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);
}


源代码链接如下:https://yunpan.cn/cvm8ihg8j3aJA (提取码:32c4)

↑ 上一篇文章:解决“错误 D8016 “/ZI”和“/Gy-”命令行选项不兼容 ”问题 关键词:错误,D8016,ZI,Gy,命令行选项,不兼容 发布日期:2016/10/9 14:34:40
↓ 下一篇文章:支持x64版本的窗口内置滚动条皮肤库源码,SkinSB皮肤滚动条库的使用 关键词:支持x64版本的窗口内置滚动条皮肤库源码 发布日期:2016/10/9 14:55:58
相关目录:.NETVC&C++软件开发
我要评论
正在加载评论信息......