用 MFC 写的小工具,一点按钮界面就白了,进度条纹丝不动,这是做 mfc进度条 时最典型的场景。原因基本只有一个:耗时循环跑在主线程上,把界面消息全堵死了。
先把控件用对
进度条控件关联一个 CProgressCtrl 变量,常用就三个方法:
m_progress.SetRange(0, 100); m_progress.SetPos(50); m_progress.StepIt();
SetRange 定范围,SetPos 设当前位置,StepIt 按步长往前走一格。范围不一定非得 0 到 100,按任务量定更自然,比如一共 300 个文件就设 0 到 300。控件本身没问题,问题出在更新它的时机。
不改架构的临时办法
在循环体里隔一段调一次 PeekMessage 泵一下消息,窗口还能勉强响应拖动,数据量小时凑合能用。缺点明显:代码丑,循环里任何一段慢下来,界面照样假死。
正经做法:放工作线程
耗时任务丢进工作线程,AfxBeginThread 或 std::thread 都行。关键规矩只有一条:线程里不许直接操作界面控件。更新进度用 PostMessage 发自定义消息给主窗口,ON_MESSAGE 映射里接,别拿系统消息凑合。主窗口收到消息再调 SetPos。顺手加个取消标志,点取消按钮置位,线程循环里检查到就退出,别让任务停不下来。
进度之外加点文字
光有进度条,用户不知道还要等多久,旁边放个静态文本,显示当前处理到第几个、百分比多少,体验立刻不一样。百分比自己算,写进 SetPos 前先夹在范围内,越界不报错但显示会怪。总量没法预估的任务,用 SetMarquee 让进度条滚动式跑,至少告诉用户程序还活着。
判断标准很简单:任务超过一秒就该上线程,别在主线程硬扛。程序退出时记得等线程收干净,不然崩在收尾阶段更难查。界面假死不是进度条的锅,是它根本没机会刷新。