一、前言在 MFC 原生开发中系统自带的CProgressCtrl进度条样式固定、自定义空间极小很难实现自定义边框、纯色填充、进度文字居中显示这类定制 UI 需求。 本文通过继承CStatic静态文本控件从零封装一个高复用自定义进度条实现效果黑色实线外边框空白区域白色底色进度区域纯绿色填充支持 0~100 区间动态切换百分比文字自动水平 垂直精准居中适配 0%/70%/100% 不同长度文本对外提供极简SetProgress接口一行代码切换进度值兼容对话框工程仅需绑定 Static 控件即可使用最终界面效果底部按钮点击切换进度进度条实时重绘更新文字与填充区域。二、整体设计思路基类选择继承CStatic对话框资源直接拖入 Static 文本控件无需自定义资源 ID 模板上手简单绘图逻辑重写WM_PAINT消息OnPaint函数分层绘制外框底色→进度填充→居中文字进度约束接口内部自动截断数值到0~100避免越界绘制文字居中核心使用GetTextExtent获取文字真实宽高通过矩形中心坐标减去文字半宽 / 半高实现完全居中摒弃固定像素偏移写法刷新机制修改进度值后调用Invalidate()触发重绘界面实时更新。三、完整控件源码3.1 头文件 ProgressStatic.h#pragma once #include afxwin.h // 自定义进度条控件继承CStatic class CProgressStatic : public CStatic { // MFC动态创建宏支持DDX_Control绑定对话框控件 DECLARE_DYNCREATE(CProgressStatic) public: CProgressStatic(); virtual ~CProgressStatic(); // 对外接口设置进度值范围0~100 void SetProgress(int nPercent); protected: int m_nPercent; // 存储当前进度数值 0~100 // 消息映射声明 DECLARE_MESSAGE_MAP() // 重绘消息响应函数 afx_msg void OnPaint(); };3.2 实现文件 ProgressStatic.cpp#include StdAfx.h #include ProgressStatic.h IMPLEMENT_DYNCREATE(CProgressStatic, CStatic) CProgressStatic::CProgressStatic() { m_nPercent 0; } CProgressStatic::~CProgressStatic() { } BEGIN_MESSAGE_MAP(CProgressStatic, CStatic) ON_WM_PAINT() END_MESSAGE_MAP() void CProgressStatic::SetProgress(int nPercent) { // 限制区间 0~100 m_nPercent max(0, min(100, nPercent)); Invalidate(); // 重绘控件 } void CProgressStatic::OnPaint() { CPaintDC dc(this); CRect rect; GetClientRect(rect); // 1. 绘制完整外黑框白底 dc.SelectStockObject(PS_SOLID); dc.SelectStockObject(WHITE_BRUSH); dc.Rectangle(rect); // 关键填充区域向内收缩1像素避免覆盖边框 CRect fillRect rect; fillRect.left 1; fillRect.top 1; fillRect.right - 1; fillRect.bottom - 1; // 根据百分比计算右侧宽度 fillRect.right fillRect.left (fillRect.Width() * m_nPercent) / 100; // 2. 只填充绿色不绘制边线 CBrush greenBrush(RGB(0, 255, 0)); dc.SelectObject(greenBrush); dc.SelectStockObject(NULL_PEN); dc.FillRect(fillRect, greenBrush); // 3. 居中文字逻辑不变 CString strText; strText.Format(_T(%d%%), m_nPercent); dc.SetTextColor(RGB(128, 0, 128)); dc.SetBkMode(TRANSPARENT); CSize textSize dc.GetTextExtent(strText); int nCenterX rect.left rect.Width() / 2; int nCenterY rect.top rect.Height() / 2; int textX nCenterX - textSize.cx / 2; int textY nCenterY - textSize.cy / 2; dc.TextOut(textX, textY, strText); }四、对话框中使用教程4.1 对话框资源布局打开对话框资源编辑器拖拽一个Static Text静态控件到界面修改 ID 为IDC_PROGRESS_BAR拉大控件矩形作为进度条容器在控件下方添加 4 个按钮ID 与文本对应IDC_BTN_100显示文字100%IDC_BTN_70显示文字70%IDC_BTN_10显示文字10%IDC_BTN_0显示文字0%4.2 对话框头文件引入控件并声明成员#include ProgressStatic.h class CMainDlg : public CDialogEx { // ... 系统自动生成代码省略 ... private: // 自定义进度条控件对象 CProgressStatic m_progressBar; protected: // 控件绑定交换函数 virtual void DoDataExchange(CDataExchange* pDX); // 按钮点击消息响应函数声明 afx_msg void OnBnClickedBtn100(); afx_msg void OnBnClickedBtn70(); afx_msg void OnBnClickedBtn10(); afx_msg void OnBnClickedBtn0(); DECLARE_MESSAGE_MAP() };4.3 对话框实现文件绑定控件 按钮事件// 控件绑定将资源Static控件关联到自定义CProgressStatic对象 void CMainDlg::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); DDX_Control(pDX, IDC_PROGRESS_BAR, m_progressBar); } BEGIN_MESSAGE_MAP(CMainDlg, CDialogEx) // 绑定四个按钮点击事件 ON_BN_CLICKED(IDC_BTN_100, CMainDlg::OnBnClickedBtn100) ON_BN_CLICKED(IDC_BTN_70, CMainDlg::OnBnClickedBtn70) ON_BN_CLICKED(IDC_BTN_10, CMainDlg::OnBnClickedBtn10) ON_BN_CLICKED(IDC_BTN_0, CMainDlg::OnBnClickedBtn0) END_MESSAGE_MAP() // 切换至100%进度 void CMainDlg::OnBnClickedBtn100() { m_progressBar.SetProgress(100); } // 切换至70%进度默认示例效果 void CMainDlg::OnBnClickedBtn70() { m_progressBar.SetProgress(70); } // 切换至10%进度 void CMainDlg::OnBnClickedBtn10() { m_progressBar.SetProgress(10); } // 切换至0%进度 void CMainDlg::OnBnClickedBtn0() { m_progressBar.SetProgress(0); }五、关键技术点解析5.1 为什么不用原生 CProgressCtrlMFC 自带CProgressCtrl仅支持水平 / 垂直简单进度无法自定义边框、填充色且原生控件不支持直接在进度条上绘制居中文字想要实现文字叠加需要额外绘图逻辑封装成本高于继承CStatic自定义绘制。5.2 文字精准居中实现原理很多新手会直接写固定像素偏移TextOut(centerX - 20, centerY - 12, str)存在严重缺陷文本长度变化时0% / 100% 宽度不同文字会左右偏移控件高度修改、字体大小调整后垂直方向不再居中。本文解决方案dc.GetTextExtent(strText)获取当前 DC 下文字真实宽高用矩形中心点减去文字一半尺寸无论文本、字体、控件尺寸如何变化文字永远水平 垂直居中。5.3 数值越界防护SetProgress中使用max(0, min(100, nPercent))强制约束进度范围传入负数自动修正为 0传入大于 100 的数值自动修正为 100 避免填充区域计算时出现负宽度、超出控件范围等绘图异常。5.4 重绘刷新机制调用Invalidate()会向控件发送WM_PAINT消息触发OnPaint重新执行全套绘图逻辑修改进度后画面实时同步更新无卡顿。六、扩展优化方向可自行拓展圆角进度条将Rectangle替换为RoundRect(rect, 8, 8)设置圆角半径实现圆角边框与填充渐变填充使用 Windows APIGradientFill实现绿色渐变进度自定义颜色接口新增SetFillColor、SetTextColor函数外部自由修改填充、文字颜色自适应字体在OnPaint中创建指定大小字体适配高分辨率界面平滑动画添加定时器分段递增进度值实现进度平滑滚动动画垂直进度条增加标志位切换水平 / 垂直填充计算逻辑。七、踩坑记录常见错误解决方案报错CRect 不存在 CenterX 成员错误写法rect.CenterX()该 API 为 Qt 接口MFC 原生 CRect 无此函数必须手动计算rect.left rect.Width() / 2文字背景白色遮挡绿色填充忘记调用dc.SetBkMode(TRANSPARENT)文字背景不透明会覆盖底层绿色区域必须设置透明背景模式修改进度后界面无变化SetProgress函数遗漏Invalidate()没有触发重绘画面不会刷新DDX_Control 绑定失败头文件未添加#include ProgressStatic.h或资源控件 ID 与DoDataExchange中 ID 不匹配。八、总结本文基于 MFCCStatic封装的自定义进度条控件代码简洁、无第三方依赖、复用性极强完美解决原生进度条自定义样式不足的痛点。核心绘图逻辑分层清晰居中文字算法通用性强可直接移植到任意 MFC 对话框工程中使用同时预留了充足扩展空间可根据项目 UI 需求自由修改颜色、圆角、动画等效果。