MFC Windows程序设计--多文档多视图

MDI:
一个文档多个视图
多个打开的文档
多个文档类型

MDI允许同时打开多个文档,SDI一个。
MDI支持多个文档类型。SDI一个。
MDI有多个菜单。SDI一般只有一个。
MDI有一个 Window菜单。
MDI一个顶层框架窗口和一个文档框架。SDI没有文档框架。

MDI的顶层窗口是CMDIFrameWnd,SDI是CFrameWnd。
MDI用CMDIChildWnd来包含文档。
MDI用CMultiDocTemplate创建文档模板。SDI用CSingleDocTemplate。

同步文档的多个视图:
Windows–New Windows,MFC调出文档模板,提取视图类,框架窗口类的CRuntimeClass*。构造一个视图和子框架窗口。视图地址被添加到文档维护的视图列表中。

CDocument::UpdateAllViews进而触发 每个CView的 CView::OnUpdate。

// 只重绘部分
void CDocument::UpdateAllView(CView *pSender, LPARAM lHint = 0L, CObject *pHint = NULL);

virtual void CView::OnUpdate(CView *pSender, LPARAM lHint, CObject *pHint);


// Document
UpdateAllViews(NULL, 1, (CObject*)pRect);
...

// View
void CMyView::OnUpdate(CView *pSender, LPARAM lHint, CObject *pHint)
{
    if(lHint == 1)
    {
        CRect *pRect = (CRect*)pHint;
        InvalidateRect(pRect);
        return;
    }
    CView::OnUpdate(pSender, lHint, pHint);
}

CMainFrame *pMainFrame = new CMainFrame;
if(!pMainFrame->LoadFrame(IDR_MAINFRAME))
{
    return FALSE;
}
m_pMainWnd = pMainFrame;


CMultiDocTemplate *pDocTemplate;
pDocTemplate = new CMultiDocTemplate
    (
        IDR_MDISQUTYPE,
        RUNTIME_CLASS(CSquaresDoc),
        RUNTIME_CLASS(CChildFrame),
        RUNTIME_CLASS(CSquaresView)
    );

资源列表包含两个图标,两个菜单,两个文档字符串。

// 支持多个文档类型
// 1.派生新视图,新文档类
// 2.新类型添加资源(图标,菜单,文档字符串)
// 3.InitInstance中
CMultiDocTemplate *pDocTemplate;
pDocTemplate = new CMultiDocTemplate
    (
        IDR_MDISQUTYPE,
        RUNTIME_CLASS(CSquaresDoc),
        RUNTIME_CLASS(CChildFrame),
        RUNTIME_CLASS(CSquaresView)
    );
AddDocTemplate(pDocTemplate);

pDocTemplate = new CMultiDocTemplate
    (
        IDR_CIRCLETYPE,
        RUNTIME_CLASS(CCirclesDoc),
        RUNTIME_CLAASS(CChildFram),
        RUNTIME_CLASS(CCirclesView)
    );
AddDocTemplate(pDocTemplate);

自定义命令消息传递
默认SDI框架窗口收到命令或UI更新消息后传递路径:
活动视图–>文档–>文档模板
框架窗口
应用程序对象

CMainFrame::OnCmdMsg(...)
{
    if(CFramWnd::OnCmdMsg(...))
    {
        return TRUE;
    }

    CWanderDoc *pDoc = (CWanderDoc*)GetActiveDocument();
    if(pDoc)
    {
        return pDoc->RouteCmdToAllViews(GetActiveView(), nID, nCode, pExtra, pHandlerInfo);
    }
}


BOOL CWanderDoc::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;
}

拆分窗口CSplitterWnd。

SDI:框架窗口–>拆分窗口–>视图。
MDI:框架窗口–>MDI文档窗口–>拆分窗口–>视图。

拆分窗口内视图,可用 CView::GetParentFrame获得视图 父窗口链上的类型为框架窗口的父窗口。

拆分窗口:
静态:最多16*16。可使用多类视图。
动态:最多2*2。可相互拆分合并。

动态拆分窗口:

BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext *pContext)
{
    return m_wndSplitter.Create(this, 2, 1, CSize(1, 1), pContext);
}

静态拆分窗口:

CSplitterWnd::CreateStatic(...)
// MFC不会自动创建静态拆分窗口中显示的视图
CSplitterWnd::CreateView(...)

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;
}

嵌套拆分窗口

// 静态嵌套
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_wndSplitter.IdFromRowCol(0, 1))
    )
    {
        return FALSE;
    }
    return TRUE;
}

动态拆分窗口拆分时,CSplitterWnd用CreateView创建新视图,传入的pContext是NULL,CreateView因此采用活动视图作为新视图的类型。和参数3指定的类型不匹配时,会出错。

// 从静态拆分窗口中嵌套动态拆分窗口----------------------------具体用时。在分析。
// 1.从CSplitterWnd派生类且
BOOL CMySplitterWnd::SplitRow(int cyBefore)
{
    GetParentFrame()->SetActiveView((CView*)GetPane(0, 0));
    return CSplitterWnd::SplitRow(cyBefore);
}

// 2.使要嵌套的动态拆分窗口,成为派生类实例。
// 带多种视图类型的动态拆分窗口。动态拆分窗口拆分时用CreateView产生新视图
BOOL CDynSplitterWnd::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);
}
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页