很多时候我们都要在UI上显示事件的进度信息,MFC也提供了现成的进度条控件CProgressCtrl,只要在UI上放置一个CProgressCtrl控件,然后在程序中调用SetRange和SetPos方法就可以显示进度信息了。但是MFC自带的控件并不是完美的,例如它无法显示文字进度,如“50%”等,只能通过进度条的位置大致判断当前的进度,如果需要详细的进度,通常还要放置一个Static控件单独显示这个百分号的进度。如果要在进度条上显示文字信息,就需要自己绘制进度条的UI了,下面我提供一个简单单但很实用的方法来满足要求。效果图如下:
 

MFC带文字显示的进度条_第1张图片

    
    首先,需要添加一个MFC类CMyProgress,继承自CprogressCtrl,这样,我们的进度条控件就具有了标准工具条的特性。为了能够反映进度信息以及绘制进度条,需要在类中添加如下变量:
    COLORREF m_prgsColor; //进度条进度部分颜色
    COLORREF m_freeColor; //进度条后面空余部分颜色
    COLORREF m_prgsTextColor; //进度部分字体颜色
    COLORREF m_freeTextColor; //空白部分字体颜色

    int  m_iMin;    //进度条的最小值,通常是0
    int  m_iMax;    //进度条的最大值,通常是100
    int  m_iPos;    //当前的进度
    int  m_nBarWidth;  //进度条宽度
   
    然后需要给调用者提供操作接口,标准的接口包括SetRange、SetPos,当然还可以提供诸如设置颜色、设置文字等方法,这个很简单,就不在这里给出代码了。

    int CMyProgress::SetPos(int nPos)
    {
        if (!::IsWindow(m_hWnd))
        {
            return -1;
        }

        int nOldPos = m_iPos;
        m_iPos = nPos;

        CRect rect;
        GetClientRect(rect);

        //这里先计算要显示的进度条宽度,避免对同一进度多次绘制窗口

        double Fraction = (double)(m_iPos - m_iMin) / ((double)(m_iMax - m_iMin));
        int nBarWidth = (int) (Fraction * rect.Width());

        if (nBarWidth != m_nBarWidth)
        {
                m_nBarWidth = nBarWidth;
                RedrawWindow();
        }

        return nOldPos;
    }

    void CMyProgress::SetRange(int nLower, int nUpper)
    {
        m_iMax = nUpper;
        m_iMin = nLower;
        m_iPos = m_iMin;
        m_nBarWidth = 0;
    }

    最后也是最重要的操作是如何绘制进度条,这需要处理WM_PAINT消息,通过VS2010或VC等我们可以很方便的添加WM_PAINT的消息处理函数,当然我们也可以自己手动在程序添加。首先,在CMyProgress中声明如下函数:
   
 afx_msg void OnPaint();
  
    然后,在CMyProgress类的定义文件中找到消息映射BEGIN_MESSAGE_MAP,然后添加映射:

    BEGIN_MESSAGE_MAP(CMyProgress, CProgressCtrl)
        ON_WM_PAINT()
    END_MESSAGE_MAP()

    OnPaint的实现方法如下:
    void CMyProgress::OnPaint()
    {
        //首先判断设置是否有效
        if (m_iMin >= m_iMax)
        {
                return;
        }

        CPaintDC dc(this); // device context for painting
        // 不为绘图消息调用 CProgressCtrl::OnPaint()


        //获取有效的进度条的位置和大小
        CRect LeftRect, RightRect, ClientRect;
        GetClientRect(ClientRect);
        LeftRect = RightRect = ClientRect;

        //计算显示进度的比例
        double Fraction = (double)(m_iPos - m_iMin) / ((double)(m_iMax - m_iMin));

 //绘制整个进度条中的有效进度
        LeftRect.right = LeftRect.left + (int)((LeftRect.right - LeftRect.left) * Fraction);
        dc.FillSolidRect(LeftRect, m_prgsColor);

 //绘制剩余进度
        RightRect.left = LeftRect.right;
        dc.FillSolidRect(RightRect, m_freeColor);

        CString str;
        str.Format(_T("%d%%"), (int)(Fraction*100.0));

        //设置文字背景颜色为透明
        dc.SetBkMode(TRANSPARENT);

        //为了能够在进度和剩余进度中显示不同颜色的字体,需要分别设置两边的字体颜色并绘图

        CRgn rgn;
        rgn.CreateRectRgn(LeftRect.left, LeftRect.top, LeftRect.right, LeftRect.bottom);
        dc.SelectClipRgn(&rgn);
        dc.SetTextColor(m_prgsTextColor);
        dc.DrawText(str, ClientRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);

        rgn.DeleteObject();
        rgn.CreateRectRgn(RightRect.left, RightRect.top, RightRect.right, RightRect.bottom);
        dc.SelectClipRgn(&rgn);
        dc.SetTextColor(m_freeTextColor);
        dc.DrawText(str, ClientRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);

        //如果在整个进度条中只显示一种字体颜色,那么可以直接调用
        //dc.SetTextColor(m_freeTextColor);
        //dc.DrawText(str, ClientRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
    }

    为了在程序中使用进度条,需要在UI上先放置一个MFC进度条控件,然后添加并关联一个CProgressCtrl变量,之后,将CProgressCtrl改写为CMyProgress即可。