在SDI框架下实现关闭文档而不退出程序的功能

本文档介绍如何在SDI框架中实现关闭文档而不退出程序的功能。通过添加关闭文件菜单,响应ID_FILE_CLOSE消息,重载OnFileClose()和OnCloseDocument()函数,以及自定义文档模板类,解决了关闭视图后框架窗口未绘制的问题,并防止新建文档时创建额外框架窗口。通过重载OpenDocumentFile()函数,确保了关闭文档后的正确行为。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在SDI框架下,一般只有退出而没有关闭文档的功能,或者说关闭了文件就退出了程序。为了实现关闭文档而不退出程序的功能,我们要做以下一些工作。

首先,如果只是实现“怎么关闭当前打开的文档,而不关闭当前程序”,还是比较简单的:
在菜单中添加一个“关闭文件”菜单,菜单ID选择ID_FILE_CLOSE,这是一个预定义的ID,也可以自己添加一个ID。在文档类中添加菜单消息响应:
这些代码是综合了CDocument::OnFileClose()和CDocument::OnCloseDocument()两个函数修改的,主要目的是实现关闭文档和视图而不关闭框架。


void C...Doc::OnClose()
{
 // TODO: 在此添加命令处理程序代码
 if (!SaveModified())
  return;

 BOOL bAutoDelete = m_bAutoDelete;
 m_bAutoDelete = FALSE; // don't destroy document while closing views
 while (!m_viewList.IsEmpty())
 {
  // get view attached to the document
  CView* pView = (CView*)m_viewList.GetHead();
  ASSERT_VALID(pView);
  pView->DestroyWindow();
 }
 m_bAutoDelete = bAutoDelete;

 // clean up contents of document before destroying the document itself
 DeleteContents();

 // delete the document if necessary
 if (m_bAutoDelete)
  delete this;
}

 

接下来的问题是这样关闭了视图之后,默认的单文档框架类没有绘制客户区,因此比较难看(试一下就知道了)。
这个问题比较简单,重载框架类的OnEraseBkgnd()函数自己绘制一个背景就可以了:

 

BOOL C...Frame::OnEraseBkgnd(CDC* pDC)
{
    // TODO: 在此添加消息处理程序代码和/或调用默认值
    CRect rcClient;
    GetClientRect(rcClient);
    pDC->FillSolidRect(rcClient, GetSysColor(COLOR_APPWORKSPACE));
    return TRUE;

    //return CFrameWnd::OnEraseBkgnd(pDC);
}

 

但是还有一个严重的问题,SDI在新建的时候会创建一个新的框架窗口。所以如果用上面的方法来关闭文档,再新建一个文档就会出来一个新的框架窗口,而旧的又不会关闭。
解决这个问题稍微复杂一些。
我们可以重载文档模板。
从CSingleDocTemplate继承一个CSingleDocTemplateEx类,在
C...App::InitInstance()中将


    CSingleDocTemplate* pDocTemplate;
    pDocTemplate = new CSingleDocTemplate(
        IDR_MAINFRAME,
        RUNTIME_CLASS(CShowBitmapDoc),
        RUNTIME_CLASS(CShowBitmapFrame),       // 主 SDI 框架窗口
        RUNTIME_CLASS(CShowBitmapView));


替换为:


    CSingleDocTemplateEx* pDocTemplate;
    pDocTemplate = new CSingleDocTemplateEx(
        IDR_MAINFRAME,
        RUNTIME_CLASS(CShowBitmapDoc),
        RUNTIME_CLASS(CShowBitmapFrame),       // 主 SDI 框架窗口
        RUNTIME_CLASS(CShowBitmapView));


要注意:不要忘了添加CSingleDocTemplateEx的头文件。
CSingleDocTemplateEx类的构造函数和析构函数按照CSingleDocTemplate类改写一下,析构函数最好为虚的。然后重载CSingleDocTemplateEx::OpenDocumentFile()函数:
这里的代码是参照CSingleDocTemplate::OpenDocumentFile()函数和CSingleDocTemplate::CreateNewDocument()、CFrameWnd::LoadFrame()改写的。

CDocument* CSingleDocTemplateEx::OpenDocumentFile(LPCTSTR lpszPathName,
    BOOL bMakeVisible)
    // if lpszPathName == NULL => create new file of this type
{
    CDocument* pDocument = NULL;
    CFrameWnd* pFrame = NULL;
    BOOL bCreated = FALSE;      // => doc and frame created
    BOOL bWasModified = FALSE;
    pFrame = (CFrameWnd*)AfxGetMainWnd();

    if (m_pOnlyDoc != NULL)
    {
        // already have a document - reinit it
        pDocument = m_pOnlyDoc;
        if (!pDocument->SaveModified())
            return NULL;        // leave the original one

        ASSERT(pFrame != NULL);
        ASSERT_KINDOF(CFrameWnd, pFrame);
        ASSERT_VALID(pFrame);
    }
    else
    {
        // create a new document
        pDocument = CreateNewDocument();
        //ASSERT(pFrame == NULL);     // will be created below
        bCreated = TRUE;
    }

    if (pDocument == NULL)
    {
        AfxMessageBox(AFX_IDP_FAILED_TO_CREATE_DOC);
        return NULL;
    }
    ASSERT(pDocument == m_pOnlyDoc);

    if (pFrame == NULL)
    {
        ASSERT(bCreated);

        // create frame - set as main document frame
        BOOL bAutoDelete = pDocument->m_bAutoDelete;
        pDocument->m_bAutoDelete = FALSE;
                    // don't destroy if something goes wrong
        pFrame = CreateNewFrame(pDocument, NULL);
        pDocument->m_bAutoDelete = bAutoDelete;
        if (pFrame == NULL)
        {
            AfxMessageBox(AFX_IDP_FAILED_TO_CREATE_DOC);
            delete pDocument;       // explicit delete on error
            return NULL;
        }
    }
    else
    {
        CCreateContext context;
        context.m_pCurrentFrame = pFrame;
        context.m_pCurrentDoc = pDocument;
        context.m_pNewViewClass = m_pViewClass;
        context.m_pNewDocTemplate = this;

        if (pFrame->CreateView(&context, AFX_IDW_PANE_FIRST) == NULL)
            return FALSE;

        //// make sure the child windows have been properly sized
        pFrame->RecalcLayout();
    }

    if (lpszPathName == NULL)
    {
        // create a new document
        SetDefaultTitle(pDocument);

        // avoid creating temporary compound file when starting up invisible
        if (!bMakeVisible)
            pDocument->m_bEmbedded = TRUE;

        if (!pDocument->OnNewDocument())
        {
            // user has been alerted to what failed in OnNewDocument
            TRACE(traceAppMsg, 0, "CDocument::OnNewDocument returned FALSE./n");
            if (bCreated)
                pFrame->DestroyWindow();    // will destroy document
            return NULL;
        }
    }
    else
    {
        CWaitCursor wait;

        // open an existing document
        bWasModified = pDocument->IsModified();
        pDocument->SetModifiedFlag(FALSE);  // not dirty for open

        if (!pDocument->OnOpenDocument(lpszPathName))
        {
            // user has been alerted to what failed in OnOpenDocument
            TRACE(traceAppMsg, 0, "CDocument::OnOpenDocument returned FALSE./n");
            if (bCreated)
            {
                pFrame->DestroyWindow();    // will destroy document
            }
            else if (!pDocument->IsModified())
            {
                // original document is untouched
                pDocument->SetModifiedFlag(bWasModified);
            }
            else
            {
                // we corrupted the original document
                SetDefaultTitle(pDocument);

                if (!pDocument->OnNewDocument())
                {
                    TRACE(traceAppMsg, 0, "Error: OnNewDocument failed after trying "
                        "to open a document - trying to continue./n");
                    // assume we can continue
                }
            }
            return NULL;        // open failed
        }
        pDocument->SetPathName(lpszPathName);
    }

    CWinThread* pThread = AfxGetThread();
    ASSERT(pThread);
    if (bCreated && pThread->m_pMainWnd == NULL)
    {
        // set as main frame (InitialUpdateFrame will show the window)
        pThread->m_pMainWnd = pFrame;
    }
    InitialUpdateFrame(pFrame, pDocument, bMakeVisible);

    return pDocument;
}


这样一来,基本很好地实现题目的要求。 

如果要效果好一些,可以像MDI一样在客户区创建一个窗口来绘制背景。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值