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