控制 MFC SDI 应用程序启动状态的简单方法






4.49/5 (18投票s)
如何在不出现闪烁伪影的情况下最小化或最大化 SDI 应用程序。
引言
我最近编写了一个 MFC SDI 应用程序,它会根据命令行参数以普通模式或最小化模式启动。我注意到,当我尝试以最小化模式初始化应用程序时,窗口会在最小化之前短暂地“闪烁”到其正常状态。在搜索后没有找到简单、优雅的解决方案后,我进入了 MFC 代码,看看是否可以找到这个问题的原因(双关语),以及是否可以在我的客户端应用程序中解决这个问题。如果您遇到过类似的问题,请继续阅读。
背景
由 MFC 应用程序向导最初创建的 MFC SDI 程序的初始化和显示由其应用程序类的 InitInstance() 方法处理。乍一看,实际显示初始化窗口的函数似乎是 ShowWindow 调用,它是 InitInstance() 的结尾。应用程序向导生成的文本注释清楚地表明了这一点,这进一步支持了这一前提。见下文
// The one and only window has been initialized, so show and update it. m_pMainWnd->ShowWindow(SW_MINIMIZE); m_pMainWnd->UpdateWindow();
那么,要更改应用程序的启动状态,似乎只需要更改 ShowWindow 中传递的参数。例如,要使应用程序最初以最小化状态显示,可以通过更改以下行来传递参数 SW_MINIMIZE
m_pMainWnd->ShowWindow(SW_SHOW);
to
m_pMainWnd->ShowWindow(SW_MINIMIZE);
在进行此更改并编译并运行程序后,我们将看到应用程序在任务栏中变为最小化之前,会短暂地以正常窗口状态闪烁。 同样,如果我们想最初以最大化状态显示窗口,并且我们修改 ShowWindow 语句以使用 SW_MAXIMIZE,我们会观察到类似的效果。之所以会发生这种效果,正如我们将在本文后面解释的那样,是因为应用程序向导中的 Microsoft 错误。我们将首先调查此问题的原因,然后提出一个非常简单的解决方案来解决该问题。
为什么会发生闪烁
为了理解为什么应用程序无法正确最小化,我们需要查看构建和显示主窗口的 MFC 代码。通过在 ProcesShellCommand 函数中设置断点,我们看到默认情况下会调用 AppWnd OnFileNew 处理程序。 OnFileNew 调用 CDocument* CDocument* CSingleDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName,BOOL bMakeVisible) OpenDocument,它创建一个新的文档实例,为该文档创建一个新的框架,最后通过调用 InitialUpdateFrame(pFrame, pDocument, bMakeVisible); 来显示窗口,并显示视图和主框架窗口。当选择与 SW_SHOW 不同的 SW 参数时,应用程序无法正确显示的原因是,InitialUpdateFrame CFrameWnd::ActivateFrame() 在函数中的窗口初始化期间调用 ShowWindow。这意味着 InitInstance() 中的 ShowWindow 调用是多余的,不需要的。
解决方案
可以使用两种解决方案来解决闪烁问题。第一个解决方案是创建 SingleDocumentTemplate 的子类,并在最小化的情况下使用 bMakeVisible = false 调用我们派生的 OpenDocument 版本。但是,这并不能解决使用 SW_MAXMIMIZE 的情况。另一种解决方案更简单,可以用于任何 ShowWIndow 模式,是在初始化窗口之前设置应用程序的 ShowWindow 属性,如下所示
        CSingleDocTemplate * pDocTemplate;
    pDocTemplate = new CSingleDocTemplate (
        IDR_MAINFRAME,
        RUNTIME_CLASS(CMyMFCProgramDoc),
        RUNTIME_CLASS(CMainFrame),       // main SDI frame window
        RUNTIME_CLASS(CMyMFCProgramView));
    AddDocTemplate(pDocTemplate);
    // Parse command line for standard shell commands, DDE, file open
    CCommandLineInfo cmdInfo;
    ParseCommandLine(cmdInfo);
        // Add the following line:
        // Set the initial window display to whatever mode you like
    this->m_nCmdShow = SW_MAXIMIZE;
        // Dispatch commands specified on the command line
    if (!ProcessShellCommand(cmdInfo))
        return FALSE;
        // The following line should be deleted since it is not needed 
        // for a SDI application that used MFC The one and only window has
        // been initialized, so show and update it.
        //m_pMainWnd->ShowWindow(SW_SHOW);
    m_pMainWnd->UpdateWindow();
    return TRUE;
有人可能会问,InitInstance 中的 ShowWindow() 行没有作用,那么 Microsoft 为什么首先将该行放在那里。答案是,如果有人决定使用 MFC 应用程序向导构建一个 SDI 应用程序,并选中不使用 MFC 的选项,则需要此行来显示窗口。但是,如果使用 MFC,Microsoft 应该删除此行。但是,由于绝大多数应用程序最初使用带有 SW_SHOW 参数的 ShowWindow 来显示,因此调用两次 ShowWindow(第一次在 ActivateFrame 中,如上所述)不会影响应用程序的显示。
历史
- First version.


