单文档多视图的实现

一个文档可能对应多个视图,最典型的如股市行情数据有图形界面和表格界面两种,实现这种程序使用MDI是最直接也是最让人接受的。这里只对可能出现的问题做一下简要分析。

1,如何显示两个视图

首先需要定义要使用的视图类,以股市软件为例(下同),定义一个图形View和一个表格View,这是必须要做的。MDI使用文档模板来加载文档、框架窗口和视图窗口,默认的创建函数在App类的InitInstance中:

CMultiDocTemplate* pDocTemplate;
 pDocTemplate = new CMultiDocTemplate(
  IDR_MY1TYPE,
  RUNTIME_CLASS(CMy1Doc),
  RUNTIME_CLASS(CChildFrame), // custom MDI child frame
  RUNTIME_CLASS(CMy1View));
 AddDocTemplate(pDocTemplate);

 

这不是我们需要的,所以我们要自己写一个CMultiDocTemplate,当然不是完全重写。例如写一个CMVDocTemplate,继承自CMultiDocTemplate。把它的构造函数写成这样:

CMVDocTemplate::CMVDocTemplate(UINT _nIDResource, 
    CRuntimeClass* _pDocClass) 
    : CMultiDocTemplate( _nIDResource, _pDocClass, NULL, NULL )
{
}

也就是说不要CChildFrame和CView,只要CDoc,这就是单文档。

然后在App类的InitInstance中将CMultiDocTemplate替换成CMVDocTemplate:

CMVDocTemplate* pDocTemplate;
 pDocTemplate = new CMVDocTemplate(
  IDR_MY1TYPE,
  RUNTIME_CLASS(CMy1Doc));
  //RUNTIME_CLASS(CChildFrame), // custom MDI child frame
  //RUNTIME_CLASS(CListViewView));
 AddDocTemplate(pDocTemplate);

 

不要CFrame和CView了,当然不能生成窗口,肯定要加到文档模板里面去。加到哪里面?什么时候加?

原来,CMultiDocTemplate有两个成员,一个是m_pFrameClass,一个是m_pViewClass,也就是文档模板构造函数中的另两个参数,加入窗口和视图类的时候改变这两个成员就行了。

再有是什么时候加,需要在打开文档或新建文档的时候加,CMultiDocTemplate有一个OpenDocumentFile函数,在这个函数里调用了CreateNewFrame,正是这个过程生成了子窗口,所以我们要重写OnOpenDocumentFile函数,以下这个过程上半部分我是抄加人书上来的,和问题关系不大。

CDocument* CMVDocTemplate::OpenDocumentFile( 
  LPCTSTR _lpszPathName, 
  BOOL _bMakeVisible )
{
    // Create the document instance and validate it.
    CDocument* pDocument = CreateNewDocument();
    if ( !pDocument )
    {
        // The document is invalid.
        // Inform the user of the error, and exit the method.
        AfxMessageBox( AFX_IDP_FAILED_TO_CREATE_DOC );
        TRACE( "Unable of creating the document in OpenDocumentFile /
       in %s at %d./n", THIS_FILE, __LINE__ );
        return NULL;
    }

    // Validate the document.
    ASSERT_VALID( pDocument );

    // The document is valid.  Set the autodelete flag.
    pDocument->m_bAutoDelete = m_bAutoDelete;

    // Verify if we are creating a new document, or opening an exesting one.
    if ( _lpszPathName == NULL )
    {
        // Xreate a new document - with default document name
        SetDefaultTitle( pDocument );

        // Avoid creating temporary compound file when starting up invisible
        if ( !_bMakeVisible )
        {
            pDocument->m_bEmbedded = TRUE;
        }

        // Initialize the document.
        if ( !pDocument->OnNewDocument() )
        {
            // Unable to initialize the document.
            TRACE( "Unable to initialize the new document in OpenDocumentFile /
     in %s at %d./n", THIS_FILE, __LINE__ );
            return NULL;
        }

  // Increment the document counter.
  m_nUntitledCount++;
    }
    else
    {
        // Open an existing document
        CWaitCursor wait;
        if ( !pDocument->OnOpenDocument( _lpszPathName ) )
        {
            // Unable to open the document.
            TRACE( "Unable to open the document in OpenDocumentFile /
     in %s at %d./n", THIS_FILE, __LINE__ );

            // We must delete the document instance.
            RemoveDocument( pDocument );
            delete pDocument;
            CloseAllDocuments( FALSE );

            return NULL;
        }
        // Set the document file path and name.
        pDocument->SetPathName( _lpszPathName );
    }
////////////////////////////////////////////////////////////////////////////////////////////////////////////

//这里是比关键的部分
 CFrameWnd* pFrameWnd = NULL;
 m_pFrameClass = RUNTIME_CLASS(CChildFrame);
 m_pViewClass = RUNTIME_CLASS(CMy1View);

 pFrameWnd = (CMDIChildWnd*)(CreateNewFrame(pDocument, NULL));
 pFrameWnd->InitialUpdateFrame(pDocument,TRUE);
 pFrameWnd->ActivateFrame(-1);

 CString str = MAKEINTRESOURCE(m_nIDResource);

 

 CFrameWnd* pFrameWnd2 = NULL;
 m_pFrameClass = RUNTIME_CLASS(CChildFrame);
 m_pViewClass = RUNTIME_CLASS(CMy1NextView);


 pFrameWnd2 = (CMDIChildWnd*)(CreateNewFrame(pDocument, NULL));
 pFrameWnd2->InitialUpdateFrame(pDocument,TRUE);
 
 pFrameWnd2->ActivateFrame(-1);
    return pDocument;
}

关键在最后这里,先使用CChildFrame和CMy1View填充那两个成员,调用CreateNewFrame,再使用CChidlFrame(也可以是别的Frame类)和CMy1NextView填充两个成员,再调用CreateNewFrame。

到这里,大功告成!我们运行一个试试,果然。

2,既然生成了多个子窗口,需要加上不同的标题,要么怎么区分?

在CView的OnInitialUpdate中写:

GetParent()->SetWindowText(“aaa”);

单这样做还不行,这里需要用一个小技巧,需要在子框架类的PreCreateWindow中添加:

cs.style &=~ (LONG) FWS_ADDTOTITLE;

一看就明白了,将窗口设置成标题自定义的。

同样改图标的话就是:

GetParent()->SetIcon(------);

3,还有一个小问题,如何在这个窗口切换到另一个窗口?

一般做法是需要查找一下带有某标题的子窗口,然后使用主框架的MDIActivate()函数。

POSITION pos = GetDocument()->GetFirstViewPosition();
 while(pos)
 {
  CWnd* pWnd = GetDocument()->GetNextView(pos)->GetParent();
  CString str;
  pWnd->GetWindowText(str);
  if(str == "aaa")
  {
   CMDIFrameWnd   *pMF=(CMDIFrameWnd*)AfxGetApp()->m_pMainWnd;   
   pMF->MDIActivate(pWnd);
  }
 }

posted @ 2013-11-06 10:36  陳さん様  阅读(307)  评论(0编辑  收藏  举报