Chapter 11 Mutiple Documents and Mutiple Views

 

                            多文档和多视图

Using splitter windows provided by MFC, a single document interface (SDI) application can present two or more views of the same document in resizeable "panes" that subdivide the frame window's client area.
 

MFC and the Multiple Document Interface

    From a user's point of view, five fundamental characteristics distinguish MDI applications from SDI applications:
    1.MDI applications permit the user to have two or more documents open for editing at once.
    2.MDI applications sometimes support multiple document types.
    3.MDI applications feature a Window menu with a New Window command for opening secondary views of a document and commands for arranging the windows in which the views appear.
    4.SDI applications generally feature just one menu.
    5.SDI applications use just one frame window—the top-level frame window that serves as the application's main window and frames views of open documents.

    The chief structural differences between MDI and SDI applications built with MFC are as follows:
    1.MDI applications derive their top-level frame window classes from CMDI-FrameWnd rather than CFrameWnd.
    2.MDI applications use classes based on CMDIChildWnd to represent the child frame windows that frame views of their documents.
    3.MDI applications use CMultiDocTemplate rather than CSingleDocTemplate to create document templates. The frame window class referenced in CMultiDocTemplate's constructor is the child frame window class rather than the top-level frame window class.
    4.MDI applications have at least two menu resources, as opposed to SDI's one. One is displayed when no documents are open; the other is displayed when at least one document is open.

Synchronizing Multiple Views of a Document

    When the New Window command is selected from the Window menu,the secondary view's address is added to the linked list of views maintained by the document object so that the document is aware that two independent views of it are visible on the screen.

    UpdateAllViews iterates through the list of views associated with the document, calling each view's virtual OnUpdate function.
    void UpdateAllViews (CView* pSender, LPARAM lHint = 0L,
CObject* pHint = NULL)
    virtual void OnUpdate (CView* pSender, LPARAM lHint,
CObject* pHint)

    A simple use for hint information is to pass the address of a RECT structure or a CRect object specifying what part of the view needs updating

     OnUpdate forwards the call to the base class if lHint is anything other than the application-specific value passed to UpdateAllViews.


    You can use UpdateAllViews' first parameter, pSender, to omit a view from the update cycle.

    Calling ProcessShellCommand in an MDI application creates a new child frame but not a top-level frame window. As a result, an MDI application must create the top-level frame window itself before calling ProcessShellCommand. The code that creates MdiSquares' main window is found elsewhere in InitInstance:
CMainFrame* pMainFrame = new CMainFrame;
if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
return FALSE;
m_pMainWnd = pMainFrame;




Supporting Multiple Document Types

    1.Derive a new document class and a new view class to serve the new document type.
    2.Add four new resources to the project for circles documents.
    3.Modify InitInstance to create a new document template containing the resource ID and CRuntimeClass pointers for the document, view, and frame window classes. Then call AddDocTemplate and pass in the address of the document template object.

    You can write SDI applications that support two or more document types, too, but the multiple document type paradigm is rarely used in single document applications.

Alternatives to MDI

workspace-based model that groups related documents in objects called workspaces and allows documents contained in a workspace to be viewed and edited in MDI-like document frames that are children of a top-level frame window. Visual C++ is one example of an application that uses the workspace containment model.


A workbook model in which individual views occupy the full client area of a top-level frame window but only one view at a time is visible. The appearance is similar to that of a maximized document frame in an MDI application. Each view is tabbed so that the user can switch from one view to another with a button click as if the views were pages in a property sheet.


A project model that groups related documents in projects but allows individual documents to be edited in SDI-like frame windows. The primary difference between the project model and the MDI and workspace models is that in the project model there is no top-level frame window providing containment for document frames


Splitter Windows

The best way to present two or more concurrent views of a document is to use a splitter window based on MFC's CSplitterWnd class

The views are children of the splitter window, and the splitter window itself is normally a child of a frame window.

A view positioned inside a splitter window can use CView::GetParentFrame to obtain a pointer to its parent frame window.

A static splitter window can contain a maximum of 16 rows and 16 columns. A dynamic splitter window is limited to at most two rows and two columns, but it can be split and unsplit interactively.

A dynamic splitter uses the same view class for all of its views unless you derive a new class from CSplitterWnd and modify the splitter's default behavior.

 

Dynamic Splitter Windows

Creating and initializing a dynamic splitter window is a simple two-step procedure:
    1.Add a CSplitterWnd data member to the frame window class.
    2.Override the frame window's virtual OnCreateClient function, and call CSplitterWnd::Create to create a dynamic splitter window in the frame window's client area.
 
  BOOL CMainFrame::OnCreateClient (LPCREATESTRUCT lpcs,
    CCreateContext* pContext)
    {
        return m_wndSplitter.Create (this, 2, 1, CSize (1, 1), pContext);
    }

 If you'd like to add a Split command to your application's menu, include a menu item whose ID is ID_WINDOW_SPLIT. This ID is prewired to the command handler CView::OnSplitCmd and the update handler CView::OnUpdateSplitCmd in CView's message map.

-----------------------------------------------------------------
Example:

UpdateAllViews (NULL, 0x7C, pLine);

void CSketchView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint)
{
    if (lHint == 0x7C) {
    CLine* pLine = (CLine*) pHint;
    ASSERT (pLine->IsKindOf (RUNTIME_CLASS (CLine)));
    CClientDC dc (this);
    OnPrepareDC (&dc);
    pLine->Draw (&dc);
    return;
    }
    CScrollView::OnUpdate (pSender, lHint, pHint);
}

void CSketchDoc::DeleteContents()
{
    int nCount = GetLineCount ();

    if (nCount) {
    for (int i=0; i<nCount; i++)
    delete m_arrLines[i];
    m_arrLines.RemoveAll ();
    }
    CDocument::DeleteContents();
}
-----------------------------------------------------------------

Static Splitter Windows

    1.Add a CSplitterWnd data member to the frame window class.
    2.Override the frame window's OnCreateClient function, and call CSplitterWnd::CreateStatic to create a static splitter window.
    3.Use CSplitterWnd::CreateView to create a view in each of the splitter window's panes.

BOOL CMainFrame::OnCreateClient (LPCREATESTRUCT lpcs,
CCreateContext* pContext)
{
    if (!m_wndSplitter.CreateStatic (this, 1, 2) ¦¦
    !m_wndSplitter.CreateView (0, 0, RUNTIME_CLASS (CTextView),
    CSize (128, 0), pContext) ¦¦
    !m_wndSplitter.CreateView (0, 1, RUNTIME_CLASS (CPictureView),
    CSize (0, 0), pContext))
    return FALSE;

    return TRUE;
}

Custom Command Routing

    SDI: Active view -> document -> document template -> frame window -> application object

    MDI: you should call the base class version of OnCmdMsg from an override to keep default command routing intact.

CFrameWnd::OnCmdMsg( ... ){
   
if (CFrameWnd::OnCmdMsg (nID, nCode, pExtra, pHandlerInfo))
    return TRUE;

    CWandererDoc* pDoc = (CWandererDoc*) GetActiveDocument ();
    if (pDoc != NULL) { // Important!
    return pDoc->RouteCmdToAllViews (GetActiveView (),
    nID, nCode, pExtra, pHandlerInfo);
}


BOOL CWandererDoc::RouteCmdToAllViews(CView *pView, UINT nID, int nCode,
void *pExtra, AFX_CMDHANDLERINFO *pHandlerInfo)
{
    POSITION pos = GetFirstViewPosition ();

    while (pos != NULL) {
        CView* pNextView = GetNextView (pos);
        if (pNextView != pView) {
            if (pNextView->OnCmdMsg (nID, nCode, pExtra, pHandlerInfo))
            return TRUE;
        }
    }
    return FALSE;
}

Three-Way Splitter Windows

静中之静
BOOL CMainFrame::OnCreateClient (LPCREATESTRUCT lpCreateStruct,
CCreateContext* pContext)
{
if (!m_wndSplitter1.CreateStatic (this, 1, 2) ¦¦
!m_wndSplitter1.CreateView (0, 0, RUNTIME_CLASS (CTextView),
CSize (128, 0), pContext) ¦¦
!m_wndSplitter2.CreateStatic (&m_wndSplitter1, 2, 1, WS_CHILD ¦
WS_VISIBLE
, m_wndSplitter1.IdFromRowCol (0, 1)) ¦¦
!m_wndSplitter2.CreateView (0, 0, RUNTIME_CLASS (CPictureView),
CSize (0, 128), pContext) ¦¦
!m_wndSplitter2.CreateView (1, 0, RUNTIME_CLASS (CPictureView),
CSize (0, 0), pContext))
return FALSE;
return TRUE;
}

静中之动
BOOL CMainFrame::OnCreateClient (LPCREATESTRUCT lpCreateStruct,
CCreateContext* pContext)
{
if (!m_wndSplitter1.CreateStatic (this, 1, 2) ¦¦
!m_wndSplitter1.CreateView (0, 0, RUNTIME_CLASS (CTextView),
CSize (128, 0), pContext) ¦¦
!m_wndSplitter2.Create (&m_wndSplitter1, 2, 1, CSize (1, 1),
pContext, WS_CHILD ¦ WS_VISIBLE ¦ WS_HSCROLL ¦
WS_VSCROLL ¦ SPLS_DYNAMIC_SPLIT,
m_wndSplitter1.IdFromRowCol (0, 1)))
return FALSE;
return TRUE;
}

    The secret to successfully nesting a dynamic splitter window inside a static splitter window involves two steps:
    1.Derive a class from CSplitterWnd, and replace CSplitterWnd::SplitRow in the derived class with the following implementation:

BOOL CNestedSplitterWnd::SplitRow (int cyBefore)
{
GetParentFrame ()->
SetActiveView ((CView*) GetPane (0, 0));
return CSplitterWnd::SplitRow (cyBefore);
}

    2.Make the nested dynamic splitter an instance of the derived class rather than an instance of CSplitterWnd.

Dynamic Splitter Windows with Multiple View Types

BOOL CDynaSplitterWnd::CreateView (int row, int col,
CRuntimeClass* pViewClass, SIZE sizeInit,
CCreateContext* pContext)
{
    if ((row == 1) && (col == 0))
    return CSplitterWnd::CreateView (row, col,
    RUNTIME_CLASS (CTextView), sizeInit, pContext);

    return CSplitterWnd::CreateView (row, col, pViewClass,
    sizeInit, pContext);
}





 

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值