6.MFC基础(六)窗口切分、文档类

本文深入解析MFC框架下切分窗口的静态与动态创建过程,包括CSplitterWnd类的使用及视图窗口的绑定。同时,详细阐述文档视图体系,涉及文档类CDocument与视图类CView之间的交互机制,以及命令消息处理流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、切分窗口

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

  运行结果:

  

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

  运行结果:

  

二、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;
}
View Code

  运行结果:

  

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

  运行结果:

  

  伪代码:

    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( ... );  //★消息处理最终目的地

        }

      }

    }

  }

转载于:https://www.cnblogs.com/csqtech/p/5693306.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值