方法三:沙漏光标为 Windows 操作系统本身默认支持之特性,微软已经把对沙漏光标的支持封装在 MFC 框架当中,开发人员可以不需要为此而在程序中引入 Cursor 资源。在 MFC 程序中,可以使用以下三种方式来操作沙漏光标:
(1)CWinApp::DoWaitCursor();
(2)CCmdTarget::BeginWaitCursor(),CCmdTarget::EndWaitCursor()和CCmdTarget::RestoreWaitCursor();
(3)CWaitCursor 封装类。
通过 Debug 跟踪可知,(2)和(3)的封装最终还是通过调用(1)来实现的。它们存在以下的对应关系:
说明 | (1) | (2) | (3) |
打开沙漏光标 | CWinApp::DoWaitCursor(1) | CCmdTarget::BeginWaitCursor() | CWaitCursor 构造函数 |
关闭沙漏光标 | CWinApp::DoWaitCursor(-1) | CCmdTarget::EndWaitCursor() | CWaitCursor 析构函数 |
刷新沙漏光标 | CWinApp::DoWaitCursor(0) | CCmdTarget::RestoreWaitCursor() | CWaitCursor::Restore() |
下面列举几个实例场景以作说明:
场景1:在开始执行长操作之前打开沙漏光标,在长操作操作结束之后关闭沙漏光标。长操作在这里是指那些相对需要比较长的时间才能执行完毕的操作,比如说大文件的复制粘贴。
{
BeginWaitCursor(); // or AfxGetApp()->DoWaitCursor(1)
// ... 执行长操作
EndWaitCursor(); // or AfxGetApp()->DoWaitCursor(-1)
}
如果在长操作的执行过程中,通过 DoModal() 方式显示了其他窗口的话,需要在 DoModal() 执行完毕之后进行沙漏光标的操作,以下代码修改自 MSDN :
{
CWaitCursor wait; // display wait cursor
// do some lengthy processing
// The dialog box will normally change the cursor to
// the standard arrow cursor.
CSomeDialog dlg;
dlg.DoModal( );
// It is necessary to call Restore here in order
// to change the cursor back to the wait cursor.
wait.Restore( );
// do some more lengthy processing
// destructor automatically removes the wait cursor
}
场景2:有一些操作或者调用,用户不可以马上从 UI 上感觉到操作已经执行,此时我们可以把光标切换沙漏光标,并且保持显示1到2秒,以提示用户操作已被执行。我所遇到最经典的案例就是客户端程序调用 IE 进行网页的跳转操作。
{
CWaitCursor wait;
::ShellExecute(GetSafeHwnd(), _T( " open " ), _T( " iexplore.exe " ), lpszUrl, NULL, SW_SHOWNORMAL);
// 保持沙漏光标至少 1 秒
::Sleep( 1000 );
}
在场景1和场景2里面,沙漏光标的打开和关闭都是在同一段连续的处理过程中进行的(在示例代码中,“同一段连续的处理过程中”其实就是“同一个函数里面”的意思),长操作会造成程序短时间内 UI 无响应,它们的实现原理实际上是在 UI 无响应之前(即长操作开始之前)先把光标置成沙漏,从而使得在 UI 无响应的这段时间内(即长操作执行过程中)光标一直呈沙漏形状。这种情况下,沙漏光标的显示时间很大程度上取决于长操作执行完毕所需要的时间,这是我们应该意识到的。
场景3:要实现对沙漏光标的精确控制,MSDN 提供了以下的示例代码:
step1:在 CMyDialog 中增加成员变量 BOOL m_bShowWaitCurosr,并在 CMyDialog 构造函数中将其初始化;
step2:增加成员函数 ShowWaitCursor() 来控制沙漏光标的打开和关闭;
//
// 函数名: CMyDialog::ShowWaitCursor
//
// 访问权: public
//
// 描述: 控制沙漏光标的打开和关闭
//
// 参数:
// bShow
// TRUE:打开沙漏光标
// FALSE:关闭沙漏光标
//
//
void CMyDialog::ShowWaitCursor(BOOL bShow)
{
if (bShow)
{
m_bShowWaitCurosr = TRUE;
BeginWaitCursor();
}
else
{
EndWaitCursor();
m_bShowWaitCurosr = FALSE;
}
}
step3:处理 WM_SETCURSOR 消息。
BOOL CMyDialog::OnSetCursor(CWnd * pWnd, UINT nHitTest, UINT message)
{
if (m_bShowWaitCurosr)
{
RestoreWaitCursor();
return TRUE;
}
return CDialog::OnSetCursor(pWnd, nHitTest, message);
}