VC 多文档用户界面设计及各个文档之间的切换

  用Delphi、VB、Windows Form(Visual C#)等称之为RAD(Rapid Application Development)的开发工具进行项目开发的都会很满足这些工具和平台提供的快速界面开发的功效:你可以很容易就可以实现一个MIS系统的管理界面,提供一个主框架,点击主框架上的某一个菜单项就打开一个处理事务的窗口,这些窗口可以重叠,可以最大/小化,一切看起来都像那么回事。但是当你在VC中进行开发的时候,发现整个世界都变了,虽然VC提供了MDI支持多文档视图的框架,但是每次你点击“打开”/“新建”菜单项的时候,你会发现新打开的窗口千孔一面,都是同一个样式,根本就不能满足项目的开发。这就需要你能够为不同的业务逻辑提供不同的显示/操作界面。这里给出一个模版,供大家参考:

  Step 1:使用VC 6.0新建一个Project,命名为:MIS。除选择单文档属性外,一切使用“默认”方式。于是你可以获得五个类:CMainFrameCMISAppCMISDocCMISView,和CAboutDlg

  Step 2:新建一套新的Doc/View/Frame:添加新类CNewDoc,基类为CDocument(方法:Insert——>New Class(Class Type:MFC Class),基类选择CDocument)。添加新的框架类CNewChildFrame,基类为CMDIChildWnd,添加方法同上。添加新的视图类CNewView,方法:Insert——>New Form,对话框中的文档类选择为CNewDoc(默认也是)。

       Step 3:将菜单资源IDR_MAINFRAME中添加菜单项“功能”,并添加菜单子项“功能1”和“功能2”,分别对应项目开发中的不同的业务逻辑。为了保证整个过程中菜单项的一致性,在IDR_MAINFRAME上Ctrl + C,然后Ctrl +V两次生成和IDR_MAINFRAME一样的菜单两个,然后将这两个菜单分别改名为IDR_MISTYPE和IDR_VIEW2_TMPL(注意:先将后两个菜单名字Copy再删除它们,然后再改名,这里菜单的名字可以换,但是为了简单就直接使用系统生成的默认菜单名字,如果改变了名字要在New CMultiDocTemplate时刻修改相应项)。

  Step 4:在CMISApp中添加变量记录这两个框架类,为简单起见就声明为public(方便后面的访问,也就懒得管面向对象的设计原则了,因为仅仅是示例而已):

public:
CMultiDocTemplate* m_pDocTemplate1;
CMultiDocTemplate* m_pDocTemplate2;

并将BOOL CMISApp::InitInstance()函数中由VC自动生成的代码作如下改变:

{  
      m_pDocTemplate2 = new CMultiDocTemplate(
            IDR_VIEW2_TMPL,          //这里就是菜单项目
            RUNTIME_CLASS(CNewDoc),          // document class
            RUNTIME_CLASS(CMDIChildWnd),         // frame class
            RUNTIME_CLASS(CNewView));        // view class
     AddDocTemplate(m_pDocTemplate2);
 

    m_pDocTemplate1 = new CMultiDocTemplate(
              IDR_MISTYPE,
              RUNTIME_CLASS(CMISDoc),
              RUNTIME_CLASS(CChildFrame), // custom MDI child frame
              RUNTIME_CLASS(CMISView));
    AddDocTemplate(m_pDocTemplate1);
}

Step 5:在MainFrm.h中添加两个文档类变量记录这里的两个不同的Document,为简单起见就声明为public

public: 
CMISDoc* m_pDoc1;
CNewDoc* m_pDoc2

注意:在CMainFrame构造函数中将上面两个变量置为NULL(否则:))。并响应IDR_MAINFRAME的两个菜单项目“功能1”和“功能2”消息,在各自的响应菜单中分别添加响应函数:

 if (m_pDoc1 != NULL)   //已经打开则激活
       {
              POSITION pos;
              pos =m_pDoc1->GetFirstViewPosition();
              CView* pView = m_pDoc1->GetNextView(pos);
              pView->GetParentFrame()->ActivateFrame();
       }
       else
       {
              CMISApp* pApp = (CMISApp*)AfxGetApp();
              m_pDoc1 = (CMISDoc*)(pApp->m_pDocTemplate1->OpenDocumentFile(NULL));
       }

和:

       if (m_pDoc2 != NULL)
       {
              POSITION pos;
              pos =m_pDoc2->GetFirstViewPosition();
              CView* pView = m_pDoc2->GetNextView(pos);
              pView->GetParentFrame()->ActivateFrame();
       }
       else
       {
              CMISApp* pApp = (CMISApp*)AfxGetApp();
              m_pDoc2 = (CNewDoc*)(pApp->m_pDocTemplate2->OpenDocumentFile(NULL));
 
       }

至此就完成了整个工作,需要说明的是:

1)  实现中由于使用到相关的类,因此在必要的地方要include相关的头文件,这里省略(编译有错误的时候添加就是了:));

2)  这里给出的是一个示例代码,实际开发中可以通过参考实现获得自己想要实现的具体应用情况(例如视图类的不同、数量不同,更重要的还有业务逻辑的不同实现等);

但是呢,到目前为止,程序有点bug,当点击功能1,再关闭,再打开,出现内存错误。这是由于功能1窗口关闭后,m_pDoc1并没有相应地置为NULL。解决方法就是在View类中添加OnDestroy消息响应:

void CMISView::OnDestroy()
{
  CView::OnDestroy(); 
// TODO: Add your message handler code here
  ((CMainFrame*)AfxGetMainWnd())->m_pDoc1 = NULL;
}
原文地址:https://www.cnblogs.com/renyuan/p/2834024.html