分类: C/C++
2008-04-23 21:39:42
如何在状态栏中实现进度指示器控制
编译/
我最近作了一个C /MFC程序,这个程序有时要加载大容量文件,为了让文件加载过程不至于太单调,我想在UI中用进度指示器显示文件的加载过程,而且我想在程序的状态栏中使用这个指示器控制。经过一番研究和尝试,我实现了自己的想法。本文将详细介绍整个实现过程。希望大家在解决类似的问题时少走一些弯路......
尽管MFC提供了标准的进度指示器控件(progress control),但是不能在状态栏里直接使用这个控件,因此我创建了自己的可重用C 类来实现进度指示。这个类从CStatusBar派生。整个实现过程不是很难,思路是在状态栏创建一个进度指示器控制,把它作为子窗口来对待,然后根据不同的状态来显示或者隐藏进度指示器。本文提供了一个范例程序pgrsbar,这个程序的框架使用了MFC的文档/视图结构,在编辑视图里显示文本文件。打开文件的时候,pgrsbar仿真长时间的加载过程并在状态栏里显示进度指示,如图一所示。我将这个含有进度指示器的状态栏封装在了一个CStatusBar派生的类中——CProgStatusBar。
图一 在状态栏里显示进度指示
下面是这个类的详细说明和使用方法:
CProgStatusBar是从标准的MFC类CStatusBar派生而来。我在CProgStatusBar派生类中加了一个CProgressCtrl类型的数据成员——m_wndProgBar,并且实现了三个重要的成员函数或方法:OnCreate、OnSize和OnProgress。下面是这三个函数的详细说明:
OnCreate负责在状态栏第一次被创建时接收控制,继而创建进度指示器并将它初始化为一个子窗口,
int CProgStatusBar::OnCreate(LPCREATESTRUCT lpcs) { lpcs->style |= WS_CLIPCHILDREN; VERIFY(CStatusBar::OnCreate(lpcs)==0); VERIFY(m_wndProgBar.Create(WS_CHILD, CRect(), this, 1)); m_wndProgBar.SetRange(0,100); return 0; }OnCreate在状态栏的式样中加了一个WS_CLIPCHILDREN,它告诉Windows不要绘制子窗口以下的状态栏区域,这样可以减少屏幕闪烁。接着OnCreate创建进度指示器控制并将它的范围设置成[0,100]。注意在这里创建进度指示器控制时没有用WS_VISIBLE,因为我想在程序开始的时候隐藏它。
// 状态栏大小改变以后,子窗口的尺寸跟着变 void CProgStatusBar::OnSize(...) { CStatusBar::OnSize(...); CRect rc; GetItemRect(0, &rc); m_wndProgBar.MoveWindow(&rc,FALSE); }CProgStatusBar::OnSize 负责移动进度指示器到你期望的位置:例子程序是把它放在了状态栏的第一个窗格,这个窗格通常用来显示程序的“就绪”信息和命令提示信息。注意这里不论进度指示器时处于可见状态还是隐藏状态,MoveWindow都照样起作用——所以即便是进度指示器处于隐藏状态,其窗口大小同样是可调的。
// 在 CProgStatusBar::OnProgress函数中 // WM_SETMESSAGESTRING 的定义在如果你愿意,完全可以创建不同的ID和消息,如用ID_DONE_LOADING表示“加载完成”,以此取代“就绪”。文件 GetParent()->PostMessage(WM_SETMESSAGESTRING,AFX_IDS_IDLEMESSAGE);
// MYWM_PROGRESS 在resource.h文件中定义 BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) ON_MESSAGE(MYWM_PROGRESS,OnProgress) END_MESSAGE_MAP() // 处理MYWM_PROGRESS消息 LRESULT CMainFrame::OnProgress(WPARAM wp, LPARAM lp) { m_wndStatusBar.OnProgress(wp); return 0; }这样想要报告进度指示的任何对象都可以通过发送一个消息到主框架来代替直接对状态栏进行调用。例如,在例子程序中,文档的Serialize函数利用Sleep函数仿真耗时加载,每隔150毫秒报告一次进度状态。通常,你肯定想让文档这样的低级对象尽可能少地包含UI代码。虽然在实践中很少有程序员遵守这一原则,但最好不用UI操作你的文档类,因为你很可能有一天想在某个服务中或命令行程序中使用它。不管怎么说,发送消息到框架总比暴露框架的内部成员要好得多。为了安全起见,文档的Serialize函数在发送消息前最好检查一下框架是否存在。如果你不想从文档发送Windows消息,可以用MFC的视图更新机制来做。你可以发明一个“暗示”代码以及一个小结构来保存进度百分比数据,并通过向框架发送MYWM_PROGRESS消息调用暗示信息。这是从文档到视图/框架传递进度控制信息的最省事的方式。