【问题标题】:How can I show a wait cursor from the moment I invoke the popup dialog until the dialog is visible to the user?从调用弹出对话框到用户可见对话框,如何显示等待光标?
【发布时间】:2018-02-21 08:07:06
【问题描述】:

所以,我想向用户显示CDialog

void CMeetingScheduleAssistantDlg::OnOptionsOutlookCalendarOptions()
{
    COutlookCalendarSettingsDlg dlgSettings(this);

    dlgSettings.DoModal();
}

现在,弹出对话框(OnInitDialog)在后台运行一个控制台应用程序。此控制台应用程序正在与 Microsoft Graph 进行通信。

因此,显示对话框可能需要几秒钟。

我用这个方法执行控制台应用程序:

bool CMeetingScheduleAssistantApp::ExecuteProgram(CString strCommand, DWORD& rExitCode)
{
    PROCESS_INFORMATION processInformation = { nullptr };
    STARTUPINFO         startupInfo = { 0 };
    int                 nStrBuffer;
    BOOL                bProcessResult, bExitCodeProcess;
    bool                bOK = false;
    CWaitCursor         wait;

    SetProgramExecuting(true);

    rExitCode = -1;

    startupInfo.cb = sizeof(startupInfo);
    nStrBuffer = strCommand.GetLength() + 50;

    bProcessResult = CreateProcess(nullptr, strCommand.GetBuffer(nStrBuffer),
        nullptr, nullptr, FALSE,
        NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW,
        nullptr, nullptr, &startupInfo, &processInformation);
    strCommand.ReleaseBuffer();

    if (!bProcessResult)
    {
        // CreateProcess() failed
        // Get the error from the system
        LPVOID lpMsgBuf;
        DWORD dw = GetLastError();
        FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
            nullptr, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, nullptr);

        // Display the error
        CString strError = (LPTSTR)lpMsgBuf;
        TRACE(_T("Authenticate failed at CreateProcess()\nCommand=%s\nMessage=%s\n\n"), strCommand, strError);

        // Free resources created by the system
        LocalFree(lpMsgBuf);

        SetProgramExecuting(false);

        // We failed.
        return false;
    }
    else
    {
        // Successfully created the process.  Wait for it to finish.
        DWORD WaitResult;
        do
        {
            WaitResult = MsgWaitForMultipleObjects(1,
                // only 1 wait object
                &processInformation.hProcess, // worker thread
                FALSE,   // stop if any
                INFINITE,  // no timeout
                QS_ALLINPUT);
            if (WaitResult == WAIT_OBJECT_0 + 1)
            {
                // Handle windows message
                MSG Msg;
                while (PeekMessage(&Msg, nullptr, 0, (UINT)-1, PM_REMOVE))
                {
                    TRACE3("%d %d %d\n", Msg.message, Msg.wParam, Msg.lParam);
                    TranslateMessage(&Msg);
                    DispatchMessage(&Msg);
                }
            }
        } while (WaitResult != WAIT_OBJECT_0);
        ASSERT(WaitResult == WAIT_OBJECT_0);

        // Get the exit code.
        bExitCodeProcess = GetExitCodeProcess(processInformation.hProcess, &rExitCode);

        // Close the handles.
        CloseHandle(processInformation.hProcess);
        CloseHandle(processInformation.hThread);

        if (!bExitCodeProcess)
        {
            // Could not get exit code.
            TRACE(_T("Executed command but couldn't get exit code.\nCommand=%s\n"), strCommand);
            SetProgramExecuting(false);
            return false;
        }

        SetProgramExecuting(false);
        return true;
    }
}

OnInitDialog 内部,就在调用ExecuteProgram 之前,我尝试使用:

CWaitCursor wait;

但这并没有什么区别。那么如何从调用弹出对话框的那一刻起直到对话框对用户可见为止显示等待光标?

【问题讨论】:

    标签: mfc modal-dialog


    【解决方案1】:

    一种解决方案是使用Modeless Dialog。您可以创建一个类似于wait cursor 对话框的对话框。

    您在代码中的 dlgSettings.DoModal(); 语句之前显示了 Modeless Dialog。请在显示Modeless Dialog 时使用TOP_MOST

    最后,一旦处理结束,从OnInitDialog() 隐藏/关闭Modeless Dialog

    另一种方法可能是:

    COutlookCalendarSettingsDlg 类中添加publicCWaitCursor* m_pWaitCursor 成员。现在修改代码为

    void CMeetingScheduleAssistantDlg::OnOptionsOutlookCalendarOptions()
    {
        COutlookCalendarSettingsDlg dlgSettings(this);
        dlgSettings->m_pWaitCursor = new CWaitCursor();
        dlgSettings.DoModal();
    }
    

    然后在返回之前将COutlookCalendarSettingsDlgOnInitDialog 修改为deleteCWaitCursor 实例。

    delete m_pWaitCursor;
    

    更新

    我想我会在此答案中添加适用于其他情况的更新。您所做的是改用CPersistantWaitCursor。文章提供了一个小例子:

    #include "PersistentWaitCursor.h"
    
    void CMyWnd::DoSomeLengthyOperation()
    {
        // Create and show the wait cursor
        CPersistentWaitCursor waitCursor;
    
        // Do some lengthy operation
        ...
    
        // waitCursor goes out of scope and cursor is restored
    }
    
    BOOL CMyWnd::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
    {
        if (CPersistentWaitCursor::WaitCursorShown())
        {
            // We are showing the wait cursor
            RestoreWaitCursor();
            return TRUE;
        }
    
        // Let the base class deal with this one
        return CWnd::OnSetCursor(pWnd, nHitTest, message);
    }
    

    查看文章以获取有关其工作原理的完整详细信息。但我可以确认,对于我其他一些冗长的操作,这个增强的CPersistantWaitCursor 起到了作用。

    【讨论】:

    • 一个有趣的方法。但是,如何处理“看起来类似于等待光标对话框的对话框”的机制?
    • @AndrewTruckle 我已经编辑了答案以包含另一种显示WaitCursor的方式
    • 这个dlgSettings->m_pWaitCursor = new CWaitCursor(); 最好在覆盖COutlookCalendarSettingsDlg::DoModal() 中完成,所以COutlookCalendarSettingsDlg 的用户不需要关心这个细节。
    • @zett42 刚刚发现了这个改进!好主意。实施的。谢谢。
    猜你喜欢
    • 2011-01-25
    • 1970-01-01
    • 1970-01-01
    • 2011-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多