一、切分窗口
1.切分窗口分类
静态切分 - 在窗口创建出来的时候就已经完成切分
动态切分 - 在程序执行过程中根据用户的需要实时完成切分,最多只能切出2*2个(最多2行2列)
2.相关类
CSplitterWnd - 父类CFrameWnd(只有一个客户区的窗口叫规则框架窗口),封装了关于不规则窗口的操作
3.静态切分
(1)定义CSplitterWnd类对象
(2)重写CFrameWnd::OnCreateClient函数
创建不规则框架窗口
BOOL CSplitterWnd::CreateStatic( CWnd* pParentWnd, //父窗口对象地址
int nRows, //行数
int nCols, //列数
DWORD dwStyle = WS_CHILD | WS_VISIBLE, //窗口风格
UINT nID = AFX_IDW_PANE_FIRST ); //ID
给不规则框架窗口的各个客户区创建视图窗口
virtual BOOL CSplitterWnd::CreateView( int row, //行
int col, //列
CRuntimeClass* pViewClass, //视图类对象地址
SIZE sizeInt, //大小
CCreateContext* pContext ); //上下文
相关代码:

#include "stdafx.h" class CMyView : public CView { DECLARE_DYNCREATE(CMyView) public: virtual void OnDraw(CDC *pDC); }; IMPLEMENT_DYNCREATE(CMyView, CView) void CMyView::OnDraw(CDC* pDC) { pDC->TextOutW(100, 100, L"自己的视图"); } class CMyFrameWnd : public CFrameWnd { public: virtual BOOL OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext); private: //定义切分窗口对象 CSplitterWnd m_split1; //日字形 CSplitterWnd m_split2; //倒日字形 }; //重写虚函数 BOOL CMyFrameWnd::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext) { //创建日字形不规则窗口,2行1列 m_split1.CreateStatic(this, 2, 1); //创建倒日字形不规则窗口,1行2列 m_split2.CreateStatic(&m_split1, 1, 2, WS_CHILD|WS_VISIBLE, m_split1.IdFromRowCol(0, 0));//将倒日字放到日字的0行0列 //给倒日字创建视图窗口 m_split2.CreateView(0, 0, RUNTIME_CLASS(CMyView), CSize(100, 100), pContext); m_split2.CreateView(0, 1, RUNTIME_CLASS(CEditView), CSize(100, 100), pContext); m_split1.CreateView(1, 0, RUNTIME_CLASS(CHtmlView), CSize(100, 100), pContext); CHtmlView *pView = (CHtmlView*)m_split1.GetPane(1, 0); pView->Navigate(L"www.baidu.com"); //设置行列信息 m_split1.SetRowInfo(0, 200, 100); //高200,最小100 m_split2.SetColumnInfo(0, 400, 200); //宽400,最小200 return TRUE; } class CMyWinApp : public CWinApp { public: virtual BOOL InitInstance(); }; CMyWinApp theApp; BOOL CMyWinApp::InitInstance() { CMyFrameWnd *pFrame = new CMyFrameWnd; m_pMainWnd = pFrame; pFrame->Create(NULL, L"MFC Split"); pFrame->ShowWindow(SW_SHOW); pFrame->UpdateWindow(); return TRUE; }
运行结果:
4.动态切分
(1)定义CSplitterWnd类对象
(2)重写CFrameWnd::OnCreateClient虚函数
CSplitterWnd::Create创建切分
先关代码:

#include "stdafx.h" class CMyView : public CView { DECLARE_DYNCREATE(CMyView) public: virtual void OnDraw(CDC *pDC); }; IMPLEMENT_DYNCREATE(CMyView, CView) void CMyView::OnDraw(CDC* pDC) { pDC->TextOutW(100, 100, L"自己的视图"); } class CMyFrameWnd : public CFrameWnd { public: virtual BOOL OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext); private: CSplitterWnd m_split; }; BOOL CMyFrameWnd::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext) { CCreateContext cct; cct.m_pNewViewClass = RUNTIME_CLASS(CMyView); m_split.Create(this, 2, 2, CSize(100, 100), &cct); return TRUE; } class CMyWinApp : public CWinApp { public: virtual BOOL InitInstance(); }; CMyWinApp theApp; BOOL CMyWinApp::InitInstance() { CMyFrameWnd *pFrame = new CMyFrameWnd; m_pMainWnd = pFrame; pFrame->Create(NULL, L"MFC Split"); pFrame->ShowWindow(SW_SHOW); pFrame->UpdateWindow(); return TRUE; }
运行结果:
二、MFC的文档
1.相关问题
文档主要提供了对数据的管理,以及和视图窗口的交互
CDocument - 父类CCmdTarget
2.相关类
(1)利用pFrame调用LoadFrame函数创建主框架窗口(之前是用的Create函数)
(2)在框架窗口的WM_CREATE消息处理中创建视图窗口
(3)在视图窗口的WM_CREATE消息处理中将视图类对象和文档类对象建立绑定关系
m_viewList.AddTail( pView );
pView->m_pDocument = this;
经过分析可知:文档类对象用一个链表成员和它关联的视图类对象,说明一个文档类对象可以对应多个视图类对象
视图类对象用一个普通成员变量保存和它关联的文档类对象,说明一个视图类对象只能对应一个文档类对象
3.对象关系图
theApp
|->m_pMainWnd(框架类对象)
|->m_pViewActive(视图类对象)
|->m_pDocument(文档类对象)
|->m_viewList(视图类对象)
4.文档类和视图类的关系
CDocument::UpdateAllViews - 刷新和文档类对象关联的所有视图类对象(视图窗口)
CView::OnUpdate - 虚函数,当视图窗口刷新时被调用
相关代码:

#include "stdafx.h" /************************************************* CMyDoc *************************************************/ class CMyDoc : public CDocument { DECLARE_MESSAGE_MAP() public: afx_msg void OnNew(); public: CString m_strDoc; }; BEGIN_MESSAGE_MAP(CMyDoc, CDocument) ON_COMMAND(ID_NEW, OnNew) END_MESSAGE_MAP() void CMyDoc::OnNew() { m_strDoc = "hello doc, 这叫老夫如何是好"; UpdateAllViews(NULL); } /************************************************ CMyView ************************************************/ class CMyView : public CEditView { DECLARE_MESSAGE_MAP() DECLARE_DYNCREATE(CMyView) public: afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); public: virtual void OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint); }; BEGIN_MESSAGE_MAP(CMyView, CEditView) ON_WM_CREATE() END_MESSAGE_MAP() IMPLEMENT_DYNCREATE(CMyView, CEditView) void CMyView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint) { CMyDoc*pDoc = (CMyDoc*)m_pDocument; SetWindowText(pDoc->m_strDoc); } int CMyView::OnCreate(LPCREATESTRUCT lpCreateStruct) { return CEditView::OnCreate(lpCreateStruct);//将视图类对象和文档类对象建立绑定关系 } /******************************************************** CMyFrameWnd *********************************************************/ class CMyFrameWnd :public CFrameWnd { DECLARE_MESSAGE_MAP() public: afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); public: virtual BOOL OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext); private: CSplitterWnd m_split; }; BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd) ON_WM_CREATE() END_MESSAGE_MAP() BOOL CMyFrameWnd::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext) { m_split.CreateStatic(this, 1, 2); m_split.CreateView(0, 0, RUNTIME_CLASS(CMyView), CSize(100, 100), pContext); m_split.CreateView(0, 1, RUNTIME_CLASS(CMyView), CSize(100, 100), pContext); return TRUE; } int CMyFrameWnd::OnCreate(LPCREATESTRUCT lpCreateStruct) { return CFrameWnd::OnCreate(lpCreateStruct);//创建视图窗口 } /***************************************************************** CMyWinAPP *****************************************************************/ class CMyWinApp : public CWinApp { public: virtual BOOL InitInstance(); }; CMyWinApp theApp; BOOL CMyWinApp::InitInstance() { CMyFrameWnd *pFrame = new CMyFrameWnd; m_pMainWnd = pFrame; CCreateContext cct; cct.m_pCurrentDoc = new CMyDoc; cct.m_pNewViewClass = RUNTIME_CLASS(CMyView); pFrame->LoadFrame(IDR_MENU1, WS_OVERLAPPEDWINDOW, NULL, &cct); pFrame->ShowWindow(SW_SHOW); pFrame->UpdateWindow(); return TRUE; }
运行结果:
5.命令消息(WM_COMMAND)的处理顺序(★★★)
View --> Document --> Frame --> App (默认顺序)
CFrameWnd::OnCmdMsg虚函数内部执行顺决定,故我们可以重写该函数,改变消息处理的顺序
相关代码:

#include "stdafx.h" /************************************************* CMyDoc *************************************************/ class CMyDoc : public CDocument { DECLARE_MESSAGE_MAP() public: afx_msg void OnNew(); public: CString m_strDoc; }; BEGIN_MESSAGE_MAP(CMyDoc, CDocument) //ON_COMMAND(ID_NEW, OnNew) END_MESSAGE_MAP() void CMyDoc::OnNew() { m_strDoc = "hello doc, 这叫老夫如何是好"; UpdateAllViews(NULL); } /************************************************ CMyView ************************************************/ class CMyView : public CEditView { DECLARE_MESSAGE_MAP() DECLARE_DYNCREATE(CMyView) public: afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); public: virtual void OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint); }; BEGIN_MESSAGE_MAP(CMyView, CEditView) ON_WM_CREATE() END_MESSAGE_MAP() IMPLEMENT_DYNCREATE(CMyView, CEditView) void CMyView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint) { CMyDoc*pDoc = (CMyDoc*)m_pDocument; SetWindowText(pDoc->m_strDoc); } int CMyView::OnCreate(LPCREATESTRUCT lpCreateStruct) { return CEditView::OnCreate(lpCreateStruct);//将视图类对象和文档类对象建立绑定关系 } /******************************************************** CMyFrameWnd *********************************************************/ class CMyFrameWnd :public CFrameWnd { DECLARE_MESSAGE_MAP() public: afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); public: virtual BOOL OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext); private: CSplitterWnd m_split; }; BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd) ON_WM_CREATE() END_MESSAGE_MAP() BOOL CMyFrameWnd::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext) { m_split.CreateStatic(this, 1, 2); m_split.CreateView(0, 0, RUNTIME_CLASS(CMyView), CSize(100, 100), pContext); m_split.CreateView(0, 1, RUNTIME_CLASS(CMyView), CSize(100, 100), pContext); return TRUE; } int CMyFrameWnd::OnCreate(LPCREATESTRUCT lpCreateStruct) { return CFrameWnd::OnCreate(lpCreateStruct);//创建视图窗口 } /***************************************************************** CMyWinAPP *****************************************************************/ class CMyWinApp : public CWinApp { DECLARE_MESSAGE_MAP() public: virtual BOOL InitInstance(); public: afx_msg void OnNew(); }; BEGIN_MESSAGE_MAP(CMyWinApp, CWinApp) ON_COMMAND(ID_NEW, OnNew) END_MESSAGE_MAP() CMyWinApp theApp; void CMyWinApp::OnNew() { AfxMessageBox(L"应用程序类处理新建消息"); } BOOL CMyWinApp::InitInstance() { CMyFrameWnd *pFrame = new CMyFrameWnd; m_pMainWnd = pFrame; CCreateContext cct; cct.m_pCurrentDoc = new CMyDoc; cct.m_pNewViewClass = RUNTIME_CLASS(CMyView); pFrame->LoadFrame(IDR_MENU1, WS_OVERLAPPEDWINDOW, NULL, &cct); pFrame->ShowWindow(SW_SHOW); pFrame->UpdateWindow(); return TRUE; }
运行结果:
伪代码:
CMyFrameWnd *pFrame = new CMyFrameWnd;
CCreateContext cct;
cct.m_pCurrentDoc = new CMyDoc;
cct.m_pNewViewClass = RUNTIME_CLASS( CMyView );
pFrame->LoadFrame( IDR_MENU1, WS_OVERLAPPEDWINDOW, NULL, &cct )
{
//this为pFrame,pContext为&cct
Create( lpszClass, strTitle, dwDefaultStyle, rectDefault, pParentWnd, ATL_MAKEINTRESOURCE(nIDResource), 0L, pContext ) )
{
1.加载菜单
//this为pFrame,pContext为&cct
CreateEx( ......, ( LPVOID )pContext ) )
{
CREATESTRUCT cs;
......
cs.lpCreateParams = lpParam; //lpParam为&cct
//创建主框架窗口,一旦执行成功,触发WM_CREATE消息,会回到我们的WM_CREATE消息处理函数中
HWND hWnd = CreateWindowEx( ......, cs.lpCreateParams );
}
}
}
***********************************************************************************************************
//处理框架窗口WM_CREATE消息,this为pFrame,lpcs为CreateWindowEx的全部参数
CFrameWnd::OnCreate( lpcs)
{
CCreateContext* pContext = ( CCreateContext* ) lpcs->lpCreateParams; //取出&cct
return OnCreateHelper( lpcs, pContext ) //this为pFrame,pContext为&cct
{
OnCreateClient( lpcs, pContext ) //this为pFrame,pContext为&cct
{
if ( pContext != NULL && pContext->m_pNewViewClass != NULL )
{
CWnd *pWnd = CreateView( pContext, AFX_IDW_PANE_FIRST )
{
//动态创建视图类对象并返回对象地址
CWnd* pView = ( CWnd* )pContext->m_pNewViewClass->CreateObject( );
//用视图类对象创建视图窗口,this为视图类对象地址pView
pView->Create( NULL, NULL, AFX_WS_DEFAULT_VIEW, CRect(0,0,0,0), this, nID, pContext )
{
return CreateEx( ......, ( LPVOID )pContext )
{
CREATESTRUCT cs;
.......
cs.lpCreateParams = lpParam;
//创建视图窗口,执行成功,触发WM_CREATE消息,会回到我们的WM_CREATE消息处理函数中
HWND hWnd = CreateWindowEx( ......, cs.lpCreateParams );
}
}
return pView;
}
if ( pWnd == NULL )
return FALSE;
}
return TRUE;
}
}
}
*********************************************************************************************************
//处理视图类的WM_CREATE消息,this为pView,lpcs为CreateWindowEx的全部参数
CEditView::OnCreate( lpcs )
{
CCtrlView::OnCreate( lpcs ) //this为pView
{
CCreateContext* pContext = ( CCreateContext* )lpcs->lpCreateParams; //拿到&cct
//this为pView,m_pCurrentDoc为文档类对象(CMyDoc)的对象地址,参数是视图类对象地址(CMyView)
pContext->m_pCurrentDoc->AddView( this )
{
m_viewList.AddTail( pView ); //文档类对象用一个链表成员和它关联的视图类对象
pView->m_pDocument = this; //视图类对象用一个普通成员变量保存和它关联的文档类对象
}
}
}
************************************************************************************************************
//文档类更新所有视图,其中会调用视图类的更新函数
UpdateAllViews( )
{
POSITION pos = GetFirstViewPosition( );//得到文档类对象中视图链表的迭代器
while ( pos != NULL )
{
CView* pView = GetNextView( pos );
if ( pView != pSender )
pView->OnUpdate( pSender, lHint, pHint ); //调用我们写的虚函数
}
}
*****************************************************************************************
//WM_COMMAND消息的处理顺序
OnCommand( wParam, lParam ) //this为pFrame
{
return CWnd::OnCommand( wParam, lParam ) //this为pFrame
{
return OnCmdMsg( nID, nCode, NULL, NULL ) //this为pFrame(起点)
{
////////////////////////////////////////////////////////////////////////////////////////////////
//视图类的WM_COMMAND处理(里面会找Document的消息处理),故View->Document
/////////////////////////////////////////////////////////////////////////////////////////////////
CView* pView = GetActiveView( );
pView->OnCmdMsg( nID, nCode, pExtra, pHandlerInfo ) //this为pView
{
CWnd::OnCmdMsg( nID, nCode, pExtra, pHandlerInfo ) //this为pView
{
//this为视图类对象地址
CCmdTarget::OnCmdMsg( ... ) //★消息处理最终目的地
{
for ( pMessageMap = GetMessageMap( ); pMessageMap->pfnGetBaseMap != NULL;
pMessageMap = (*pMessageMap->pfnGetBaseMap)( ) ) { ...... }
}
}
if ( m_pDocument != NULL )
{
return m_pDocument->CDocument::OnCmdMsg( nID, nCode, pExtra, pHandlerInfo ) //this为文档对象地址
{
//this为文档类对象地址
CCmdTarget::OnCmdMsg( ...... ); //★消息处理最终目的地
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
//框架类的WM_COMMAND消息处理
////////////////////////////////////////////////////////////////////////////////////////////////
CWnd::OnCmdMsg( nID, nCode, pExtra, pHandlerInfo ) //this为pFrame
{
//this为框架类对象地址
CCmdTarget::OnCmdMsg( ... ); //★消息处理最终目的地
}
////////////////////////////////////////////////////////////////////////////////////////////////
//应用程序类的WM_COMMAND的消息处理
////////////////////////////////////////////////////////////////////////////////////////////////
CWinApp* pApp = AfxGetApp( );
pApp->OnCmdMsg( nID, nCode, pExtra, pHandlerInfo )
{
//this为应用程序对象地址
CCmdTarget::OnCmdMsg( ... ); //★消息处理最终目的地
}
}
}
}