1 进度条和定时器联用
1.1 进度条控件的添加
在对应界面的界面中拖入进度条控件并对其 ID 进行修改。修改 ID 是为了方便后期的代码编写时的代码可读性可降低后期维护的难度。具体操作如下图所示。
1.2 绑定控件
首先要导入相应的头文件。进度条这个类的类名是 CProgressBarCtrl ,定义在 atlctrls.h 的 6703 行。
#include <atlctrls.h>
然后是在相应的界面 .h 文件中创建进度条对象用于接收界面的控件对象。
CProgressBarCtrl _CPGBC;
最后是在相应的界面 .cpp 文件中获取控件对象,用我们上一步定义的对象去接收,这一步便是绑定。
// 1. 根据控件 ID 来获取界面上的控件对象
_CPGBC = GetDlgItem(IDC_PROGRESS1);
// 2. 然后就可以对该控件进行操作,这里是设置进度条的范围
_CPGBC.SetRange32(0,100);
1.3 定时器的引入
经过上面两个步骤,我们已经创建出了一个进度条控件并与后台代码进行了绑定,还给进度条设置了相应的最大值和最小值。但也也能观察到,这个进度条不会显示进度,那么怎么让它动起来呢?
为了达到这个目的,我们要引入定时器的概念。如果有接触过 Qt 等其他界面开发的话应该会对这个名词感到熟悉,其实说来也很简单,就是用来设置一个循环,然后我们在循环里写了一个函数,函数中有相应的操作。而定时器所担任的角色就是这个无限循环和睡眠,也就是说,相隔一定的时间之后再次执行一次这个函数。
那么,我们要做的是先在代码中调用这个定时器,并进行相应设置。我们在第 1.2 中的最后设置进度条范围的代码后加上下面的这一行进行定时器引用。
SetTimer(LOADING_TIMER, 100, NULL);
关于这个函数的参数,我们可以先来看看它的函数原型:
/*
** 参数说明:
** @param : _In_ UINT_PTR nIDEvent 定时器 ID
** @param : _In_ UINT nElapse 定时器的时间间隔,即隔多少时间进行一次循环
** @param : _In_opt_ void (CALLBACK* lpfnTimer)(HWND, UINT, UINT_PTR, DWORD) // 定时器回调函数
*/
UINT_PTR SetTimer(
_In_ UINT_PTR nIDEvent,
_In_ UINT nElapse,
_In_opt_ void (CALLBACK* lpfnTimer)(HWND, UINT, UINT_PTR, DWORD) = NULL) throw()
{
ATLASSERT(::IsWindow(m_hWnd));
return ::SetTimer(m_hWnd, nIDEvent, nElapse, (TIMERPROC)lpfnTimer);
}
参数应该都比较简单,最后一个参数 void (CALLBACK lpfnTimer)(HWND, UINT, UINT_PTR, DWORD) 就是定时器的回调函数,即我的定时器每隔多少时间要执行什么样的操作,而这些操作就在这个回调函数中编写。
那么这个定时器 ID 是什么呢?对于这个唯一标识(一个定时器就只有一个 ID),我们需要自己定义。我们在界面的 .h 文件中加入以下定义:
#define LOADING_TIMER (WM_USER+5556)
这里我们定义了一个 LOADING_TIMER 宏作为我们定时器的唯一 ID ,它的值为 (WM_USER+5556) ,至于这个值是如何而来其实我们不需要知道,只需要记住,当以后我们再进行宏值定义时也是这个样子,宏值在 WM_USER 的基础上加上某个数值,但是这个值也不要过大,也不能过小。如果发现定义的宏值好像不能达到我们所预期的效果时,我们可以对它的值进行适当增大或减小,多半是与系统自带的值相冲突了,错开即可。
1.4 定时器响应事件
接下来我们需要在界面 .h 文件中进行消息映射的处理,类似于 Qt 中的信号和槽函数的绑定。定时器的消息映射是一个消息通知,所以我们在消息映射宏的位置添加如下代码:
BEGIN_MSG_MAP(CAboutDlg)
MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
MESSAGE_HANDLER(WM_TIMER, OnTimer); // 这个是新加的,其他的是系统生成的
COMMAND_ID_HANDLER(IDOK, OnCloseCmd)
COMMAND_ID_HANDLER(IDCANCEL, OnCloseCmd)
END_MSG_MAP()
如上面的代码块,我们加入了一个消息通知宏函数即可,传入我们自定义的定时器 ID 和定时器的响应函数指针 OnTimer ,这样一来就相当于把定时器和这个函数进行绑定了。
那么接下来就是定时器响应函数的代码编写了。首先在界面 .h 文件中添加这个函数定义:
LRESULT OnTimer(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled);
这个是系统给出的函数示例,一共有 3 种,分别对应消息映射的类型,我们根据我们要的消息映射类型进行选择,复制粘贴后改名即可。下面是系统给出的函数示例:
// Handler prototypes (uncomment arguments if needed):
// 1. Message
LRESULT MessageHandler(UINT /*uMsg*/,
WPARAM /*wParam*/,
LPARAM /*lParam*/,
BOOL& /*bHandled*/)
// 2. Command
LRESULT CommandHandler(WORD /*wNotifyCode*/,
WORD /*wID*/,
HWND /*hWndCtl*/,
BOOL& /*bHandled*/)
// 3. Notify
LRESULT NotifyHandler(int /*idCtrl*/,
LPNMHDR /*pnmh*/,
BOOL& /*bHandled*/)
函数声明添加完成之后,我们在相应的 .cpp 文件中编写功能代码,具体实现如下:
LRESULT CAboutDlg::OnTimer(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
{
// 判断是不是我们自定义的那个定时器的 ID
if (LOADING_TIMER == wParam)
{
// 获取当前进度条的位置
UINT curPos = _CPGBC.GetPos();
// 未超过最大值,前进一步
if (curPos < 100)
{
_CPGBC.SetPos(++curPos);
}
// 不在范围之内,归 0
else
{
_CPGBC.SetPos(0);
}
}
// 消息已经处理完毕
bHandled = TRUE;
return 0;
}