MFC中的view, document 和Frame是通过DocTemplate联系起来的, 但是如果我用splitter来进行静态窗口分割, 形成三个View (CMyViewA, CMyViewB, CMyViewC), 如何把这三个view都跟document关联起来呢.一般程序都是这么写的:
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CGraphDoc),
RUNTIME_CLASS(CMainFrame),       // main SDI frame window
RUNTIME_CLASS(CMyViewA));
if (!pDocTemplate)
return FALSE;
AddDocTemplate(pDocTemplate);但上面的代码如何使Document和CMyViewB, CMyViewC 关联在一起呢. 我查找MFC的源代码也没发现他们之间的关系.
请大家解释一下,谢谢了

解决方案 »

  1.   

    CreateView的时候有个CCreateContext参数,其中m_pCurrentDoc就表明了当前view和哪个doc关联。
    和DocTemplate无关。多搞doctemplate可以让你在新建时,选择用那个模版。
      

  2.   


     pDocTemplate = new CSingleDocTemplate(
            IDR_MAINFRAME,
            RUNTIME_CLASS(CGraphDoc),
            RUNTIME_CLASS(CMainFrame),       // main SDI frame window
            RUNTIME_CLASS(CMyViewA));
     pDocTemplate2 = new CSingleDocTemplate(
            IDR_MAINFRAME,
            RUNTIME_CLASS(CGraphDoc),
            RUNTIME_CLASS(CMainFrame),       // main SDI frame window
            RUNTIME_CLASS(CMyViewB));
     pDocTemplate3 = new CSingleDocTemplate(
            IDR_MAINFRAME,
            RUNTIME_CLASS(CGraphDoc),
            RUNTIME_CLASS(CMainFrame),       // main SDI frame window
            RUNTIME_CLASS(CMyViewC));
      

  3.   

    先在项目添加一个View类,从需要的父View类派生,如CMyView2。然后在 OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)里面,
    m_wndSplitter.CreateStatic(this, 1, 2);
    把框架分成1行2列的分裂窗口。再m_wndSplitter.CreateView(0, 0, pContext->m_pNewViewClass, CSize(xxx,yyy), pContext)
    创建0行0列的View,pContext->m_pNewViewClass是你原来的View,CSize指定大小。然后m_wndSplitter.CreateView(0, 1,RUNTIME_CLASS(CMyView2), CSize(xxx,yyy), pContext)
    用新View创建0行1列的View。
      

  4.   

    ndy_w 谢谢你的解答,这么说, 在 new CSingleDocTemplate 的时候,第三个参数随便写一个view就可以了,是不是? 
      

  5.   

    你创建的是个SDI程序,你其它的view是不直接和doc类给关联的,除非你创建MDI程序。
    如果一定要是SDI,你可以通过主框架窗口得到doc类,得到数据以后传递到你指定的view中。
      

  6.   

    visualEleven兔子部长,我试了一下, 其他的view和 doc类也是直接关联的, 直接用GetDocument()就可以取出相对应的Doc类来。 我觉得还是ndy_w说的对。  
    这样看来, 对于用splitter来拆分的窗口来说, CSingleDocTemplate构造函数中的最后一个RUNTIME_CLASS参数并没有什么意义,是不是这样? 大家再讨论一下 。
      

  7.   

    有意义的,它描述用哪个view来表现doc,比如新建,打开等等。当然你把这些全部重载了也可以不用
      

  8.   

    看了一下,if (!pWnd->Create(NULL, NULL, dwStyle,
    rect, this, IdFromRowCol(row, col), pContext)) 这句调用确实是将view和doc关联起来了BOOL CSplitterWnd::CreateView(int row, int col,
    CRuntimeClass* pViewClass, SIZE sizeInit, CCreateContext* pContext)
    {
    #ifdef _DEBUG
    ASSERT_VALID(this);
    ASSERT(row >= 0 && row < m_nRows);
    ASSERT(col >= 0 && col < m_nCols);
    ASSERT(pViewClass != NULL);
    ASSERT(pViewClass->IsDerivedFrom(RUNTIME_CLASS(CWnd)));
    ASSERT(AfxIsValidAddress(pViewClass, sizeof(CRuntimeClass), FALSE)); if (GetDlgItem(IdFromRowCol(row, col)) != NULL)
    {
    TRACE2("Error: CreateView - pane already exists for row %d, col %d.\n",
    row, col);
    ASSERT(FALSE);
    return FALSE;
    }
    #endif // set the initial size for that pane
    m_pColInfo[col].nIdealSize = sizeInit.cx;
    m_pRowInfo[row].nIdealSize = sizeInit.cy; BOOL bSendInitialUpdate = FALSE; CCreateContext contextT;
    if (pContext == NULL)
    {
    // if no context specified, generate one from the currently selected
    //  client if possible
    CView* pOldView = (CView*)GetActivePane();
    if (pOldView != NULL && pOldView->IsKindOf(RUNTIME_CLASS(CView)))
    {
    // set info about last pane
    ASSERT(contextT.m_pCurrentFrame == NULL);
    contextT.m_pLastView = pOldView;
    contextT.m_pCurrentDoc = pOldView->GetDocument();
    if (contextT.m_pCurrentDoc != NULL)
    contextT.m_pNewDocTemplate =
      contextT.m_pCurrentDoc->GetDocTemplate();
    }
    pContext = &contextT;
    bSendInitialUpdate = TRUE;
    } CWnd* pWnd;
    TRY
    {
    pWnd = (CWnd*)pViewClass->CreateObject();
    if (pWnd == NULL)
    AfxThrowMemoryException();
    }
    CATCH_ALL(e)
    {
    TRACE0("Out of memory creating a splitter pane.\n");
    // Note: DELETE_EXCEPTION(e) not required
    return FALSE;
    }
    END_CATCH_ALL ASSERT_KINDOF(CWnd, pWnd);
    ASSERT(pWnd->m_hWnd == NULL);       // not yet created DWORD dwStyle = AFX_WS_DEFAULT_VIEW;
    if (afxData.bWin4)
    dwStyle &= ~WS_BORDER; // Create with the right size (wrong position)
    CRect rect(CPoint(0,0), sizeInit);
    if (!pWnd->Create(NULL, NULL, dwStyle,
    rect, this, IdFromRowCol(row, col), pContext)) // 这里确实关联起来了
    {
    TRACE0("Warning: couldn't create client pane for splitter.\n");
    // pWnd will be cleaned up by PostNcDestroy
    return FALSE;
    }
    ASSERT((int)_AfxGetDlgCtrlID(pWnd->m_hWnd) == IdFromRowCol(row, col)); // send initial notification message
    if (bSendInitialUpdate)
    pWnd->SendMessage(WM_INITIALUPDATE); return TRUE;
    }
      

  9.   

    谢谢ndy_w和兔子部长的解释, 都很正确了。我也仔细看了看MFC相关的源代码,现在已经比较明白了。
    网上有个更彻底的解释,我在这里贴出来吧, 供有同样疑问的tx参考:SDI means "Single Document Interface", it restricts you to only one single document at a time but not in the number of views you can open for this document.The probably most common approach to open multiple views in an SDI application are Splitter Windows.You add one view to the CSingleDocTemplate (it doesn't matter which one)pDocTemplate = new CSingleDocTemplate(
        IDR_MYRESOURCEID,
        RUNTIME_CLASS(CMyDoc),
        RUNTIME_CLASS(CMyFrameWnd),
        RUNTIME_CLASS(CMyListView));Your frame window gets an instance of a CSplitterWnd m_wndSplitter and you overload the OnCreateClient virtual function:BOOL CMyFrameWnd::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext) 
    {
        VERIFY(m_wndSplitter.CreateStatic(this,1,2)); // one row / two columns    VERIFY(m_wndSplitter.CreateView(0,0,RUNTIME_CLASS(CMyListView),
            CSize(300,300),pContext));
        VERIFY(m_wndSplitter.CreateView(0,1,RUNTIME_CLASS(CMyEditView),
            CSize(300,300),pContext));    return TRUE;
    }This example creates a splitter window with one row and two columns. On the left side in the splitter is a view of type CMyListView and on the right side is a view of type CMyEditView.You can even nest multiple splitter windows one in each other to create arbitrary complex view collections in the frame window.Here is a small tutorial which shows how to work with splitter windows in a SDI application:http://www.codeproject.com/KB/splitter/splitterwindowtutorial.aspxEditWiring up of the views you add to the splitter with the document does MFC internally: CCreateContext* pContext which is passed into OnCreateClient contains a reference m_pCurrentDoc to the current document (the Framewindow knows about this document). MFC uses this in CView::OnCreate (ViewCore.cpp) to add the View to the Document: m_pCurrentDoc->AddView(this) and to set the document pointer m_pDocument in the View.Therefore subsequent calls of UpdateAllViews of your document will take care of both views.