深入了解MFC中的文挡/视框架

深入了解MFC中的文挡/视框架

转自:http://www.huinu.com/viewnews-42294.html

 Visual C++ 6.0 以其功能强大、用户界面友好而倍受程式员们的青睐。但是,在当前的Microsoft 基本类库4.2 版本中,大约有将近200 个类,数千个参数,加之Microsoft 公司隐藏了一部份技能细节,使得大家深入研究MFC变得十分困难。
  MFC的AppWizard能够生成三种类别的实际运用程式:基于对话框的实际运用、单文档实际运用(SDI)和多文档实际运用(MDI)。前两者的框架较简单,本文不再赘叙。笔者拟从MFC中的文档/视框架入手,考虑一部份参数的程式,并解决编制MDI 实际运用程式过程中的一部份日常学识疑难问题。
(一)、了解文档/视框架
  MFC实际运用程式模型历经多年以有了相当大的进展。有唯一时期,它只是个应用实际运用程式对象和主窗口对象的简单模型。在那个模型中,实际运用程式的数据作为成员变量保持在框架窗口类中,在框架窗口的客户区中,该数据被提交呈现器。随着MFC2。0的问世,一种实际运用程式框架的新方法----MFC文档/视框架呈现了。在这种框架中,CFrameWnd繁重的任务被委派给几个不相同类,呈现了数据存储和呈现的分离。一般情况下,采用文档/视框架的实际运用程式至少应由以下对象组成:
。实际运用程式是唯一CwinApp派生对象,它充当全部实际运用程式的容器。实际运用程式沿消息映射互联网分配消息给它的所有子程式。
。框架窗口是一CfrmeWnd派生对象。
。文档是唯一CDocument派生对象,它存储实际运用程式的数据,并把这一部份消息帮助给实际运用程式的其余部份。
。视窗是Cview派生对象,它与其父框架窗口用户区对齐。视窗接受用户对实际运用程式的输入并呈现有联系联的文档数据。
通常,实际运用程式数据存在于简单模型中的框架窗口中。在文档/视方法中,该数据移入称为document的独立数据对象。当然,文档不一定是文字,文档是能够表现实际运用程式应用的数据集的抽象术语。而用户输入处理及图形输出功能从框架窗口转向视图。单独的视窗完全遮蔽框架窗口的客户区,这意味着即使程式员直接绘画至框架窗口的客户区,视图仍遮蔽绘画,在屏幕上不呈现任何消息。所以输出必需经过视图。框架窗口仅仅是个视图容器。
CDocument类对文档的建立及归档帮助支持并帮助实际运用程式用于控制其数据的接口。MDI实际运用程式能够处理多个类别的文档,每个类别的文档拥有唯一有联系联的文档模板对象。文档对象驻留在场景后面,帮助由视图对象呈现的消息。文档至少有唯一有联系联的视图。视图只能与唯一文档有联系联。
在文档/视方法中,对象的建立是由文档模板来管制的,它是CDocTemplate派生对象,建立并维修框架窗口,文档及视。
MFC调用命令处理程式以响应除了在实际运用程式中的事件。命令发送的优先级是:
聚会的视图->框架窗口->文档->实际运用程式->默认窗口过程(DefWindowsProc)
总之,在文档/视方法中,文档和视是分离的,即:文档用于保存数据,而视是用来呈现这一部份数据。文档模板维修它们之间的关西。这种文档/视框架在开发大型热门软件项目时特别有用。
(二)、了解与文档/视框架有关的各种类之间的联系。
在文档/视实际运用程式中,CWinApp对象拥有并控制文档模板,后者产生文档、框架窗口及视窗。这种相互联系如图(1)所示:
从用户的角度来看,“视”实际上是唯一普通的窗口。象更多有联系基于Widnows实际运用的窗口相同,大家能够改变它的尺寸,对它停止移动,也能够随时关闭它。若从程式员的角度来看,视实际上是唯一从MFC类库中的Cview类所派生出的类的对象。文档对象是用来保存数据的,而视对象是用来呈现数据的,并且允许对数据停止编辑。SDI或MDI的文档类是由Cdocument类派生出来的,它能够有唯一或多个视类,而这一部份视类最终都是由Cview类派生出来的。视对象只有唯一与之相联系的文档对象,它所包含的CView::GetDocument参数允许实际运用在视中得到与之相联系的文档,据此,实际运用程式能够对文档类成员参数及社交数据成员停止来访。假如视对象接受到了一条消息,表明用户在编辑控制中输入了新的数据,此时,视就必需通知文档对象对其内部数据停止相应的更新。
假如文档数据除了了变化,则所有的视都必需被通知到,以便它们能够对所呈现的数据停止相应的更新。Cdocument::UpdateAllViews参数即可完成此功能。当该参数被调用时,派生视类的CView::OnUpdate参数被触发。通常OnUpdate参数要对文档停止来访,读取文档数据,然后再对视的数据成员或控制停止更新,以便反映出文档的变化。另外,还能够利用OnUpdate参数使视的部份客户区无效,以便触发Cview::OnDraw参数,利用文档数据来重新对窗口停止绘制。
在MDI实际运用程式中,能够处理多个文档类别,即多个文档模板,每个模板又能够有多个文档,每个文档又能够多视呈现。为管制方便,上一级往往保留了下一级的指针列表。如图(2)所示:
解释如下:
(1)、每个实际运用程式类(CwinApp的派生类)都保留并维修了一份所有文档模板的指针列表,这是唯一链表框架。实际运用程式为所要支持的每个文档类别动态分配唯一CMultiDocTemplate 对象,
CmultiDocTemplate(UINT nIDResource,
CruntimeClass * pDocClass,
CruntimeClass * pFrameClass,
CruntimeClass * pViewClass );
并在实际运用程式类的CWinApp::InitInstance成员参数中将每个CMultiDocTemplate对象传递给CWinApp::AddDocTemplate。 该参数将唯一文档模板加入到实际运用程式可用文档模板的列表中。参数原形为:
void AddDocTemplate(CdocTemplate * pTemplate);
实际运用程式能够用CWinApp::GetFirstDocTemplatePostion获得实际运用程式注册的第唯一文档模板的位置,利用该值来调用CWinApp::GetNextDocTemplate参数,获得第唯一CDocTemplate对象指针。参数原形如下:
POSITION GetFirstDocTemplate( ) const;
CDocTemplate *GetNextDocTemplate( POSITION & pos ) const;
第二个参数返回由pos 标识的文档模板。POSITION是MFC定义的唯一用于迭代或对象指针检索的值。经过这两个参数,实际运用程式能够遍历整个文档模板列表。假如被检索的文档模板是模板列表中的最后唯一,则pos参数parameter被置为NULL。
(2)、唯一文档模板能够有多个文档,每个文档模板都保留并维修了唯一所有对应文档的指针列表。实际运用程式能够用CDocTemplate::GetFirstDocPosition参数获得与文档模板有联系的文档集合中第唯一文档的位置,并用POSITION值作为CDocTemplate::GetNextDoc的参数parameter来重复遍历与模板有联系的文档列表。参数原形为:
viaual POSITION GetFirstDocPosition( ) const = 0;
visual Cdocument *GetNextDoc(POSITION & rPos) const = 0;
假如列表为空,则rPos被置为NULL.
(3)、在文档中能够调用CDocument::GetDocTemplate获得指向该文档模板的指针。参数原形如下:
CDocTemplate * GetDocTemplate ( ) const;
假如该文档不属于文档模板管制,则返回值为NULL。
(4)、唯一文档能够有多个视。每唯一文档都保留并维修唯一所有有联系视的列表。CDocument::AddView将唯一视连接到文档上,将该视加入到文档相联系的视的列表中,并将视的文档指针指向该文档。当有File/New、File/Open、Windows/New或Window/Split的命令而将唯一新创建的视的对象连接到文档上时, MFC会自动调用该参数,框架经过文档/视的框架将文档和视联系起来。当然,程式员也能够根据自个的需求调用该参数。
Virtual POSITION GetFirstViewPosition( ) const;
Virtual CViw * GetNextView( POSITION &rPosition) cosnt;
实际运用程式能够调用CDocument::GetFirstViewPosition返回与调用文档相联系的视的列表中的第唯一视的位置,并调用CDocument::GetNextView返回指档次置的视,并将rPositon的值置为列表中下唯一视的POSITION值。假如找到的视为列表中的最后唯一视,则将rPosition置为NULL.
当在文档上新增唯一视或删除唯一视时,MFC会调用OnChangeViewList参数。假如被删除的视是该文档的最后唯一视,则删除该文档。
(5)、唯一视只能有唯一文档。在视中,调用CView::GetDocument能够获得唯一指向视的文档的指针。参数原形如下:
CDocument *GetDocument ( ) const;
假如该视不与任何文档相,则返回NULL.
(6)、MDI框架窗口经过调用CFrameWnd::GetActiveDocument 能够获得与当前聚会的视相连的CDocument 指针。参数原形如下:
virtual CDocument * GetActiveDocument( );
(7)、经过调用CFrameWnd::GetActiveView 能够获得指向与CFrameWnd框架窗口连接的聚会视的指针,假如是被CMDIFrameWnd框架窗口调用,则返回NULL。MDI框架窗口能够首先调用MDIGetActive找到聚会的MDI子窗口,然后找到该子窗口的聚会视。参数原形如下:
virtual Cdocument * GetActiveDocument( );
(8)、MDI框架窗口经过调用CFrameWnd::GetActiveFrame, 能够获得唯一指向MDI框架窗口的聚会多文档界面子窗口的指针。
(9)、CMDIChildWnd调用GetMDIFrame获得MDI框架窗口(CMDIFrameWnd)。
(10)、CWinApp 调用AfxGetMainWnd得到指向实际运用程式的聚会主窗口的指针。
下面一段代码Code,就利用CDocTemplate、CDocument和CView之间的存取联系,遍历整个文档模板、文档以及视。
CMyApp * pMyApp = (CMyApp *)AfxGetApp();
POSITION p = pMyApp->GetFirstDocTemplatePosition();
while(p!= NULL) {
CDocTemplate * pDocTemplate = pMyApp->GetNextDocTemplate(p);
POSITION p1 = pDocTemplate->GetFirstDocPosition();
while(p1 != NULL) {
CDocument * pDocument = pDocTemplate->GetNextDoc(p1);
POSITION p2 = pDocument->GetFirstViewPosition();
while(p2 != NULL) {
CView * pView = pDocument->GetNextView(p2);
}
}
}
(图4)、遍历整个文档模板、文档和视
在实际运用程式的任何地方,程式员都能够调用AfxGetApp( )获得实际运用程式的对象指针。由于本文着重介绍文档/视的联系,至于框架窗口之间的联系没能列全,读者能够查相应的文档。
(三)、了解CwinApp::OnFileNew、CwinApp::OnFileOpen和Window/New的程式程式。
(1)、CwinApp::OnFileNew和CwinApp::OnFileOpen参数的简单程式。
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
在CWinApp::OnFile/new 或CwinApp::OnFileOpen参数中,核心操作是CDocTemplate::OpenDocument参数。其参数原型为:
virtual CDocument* CDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName, BOOL bMakeVisible = TRUE ) = 0;
图(4)中星号标注之后即是该参数的程式,简要介绍如下:
(1)、CDocTemplate::CreateNewDocument参数创建唯一新文档,其类别与文档模板有联系,并经过参数CDocTemplate::AddDocument加入该文档模板的文档指针列表中。此时,文档类的构造参数被执行,程式能够在此停止文档的初始化。
(2)、参数CDocTemplate::CreateNewFrame调用MDI子窗口类(CMDIChildWnd)的构造参数,生成MDI子窗口对象。接着调用CMDIChildWnd::PreCreateWindow。然后,生成唯一CCreateContext对象,(CcreateContext是MFC框架所应用的一种框架,它将构成文档和视的组件联系起来。后文将详细介绍之。)并将该对象值传给CMDIChildWnd::OnCreateClient参数。MFC调用此参数,用CCreateContext对象帮助的消息创建唯一或多个CView对象。此时,各视的构造参数被依次调用。
(3)、接着,判断lpszPathName也许为空。分为两种情况:
(a)、若为空,则表明要创建唯一新文档:调用SetDefaultTitle参数装载文档的缺省题目,并呈现在文档的题目栏中;然后执行CDocument::OnNewDocument。该参数调用DeleteContents以保证文档为空,然后置新文档为清洁。能够重载该参数。
(b)、否则,表明要打开唯一已存在的文档:调用CDocument::OnOpenDocument打开指定的文档;执行DeleteContext,保证文档为空;调用CObject::Serialize读入该文档的内容。(程式员可在此停止文档的读入操作。当然,也能够在CDocument::OnOpenDocument中读入文档)。然后置文档为清洁;最后,调用CDocTemplate::SetPathName,并把文档名加入到最近文档列表中。
(4)、调用CDocTemplate::InitialUpdateFrame参数,使框架窗口中的各个视收到OnInitialUpdate调用。框架窗口的主视(子窗ID等于AFX_IDW_PANE_FIRST的视)被激活。程式员能够在此对视对象停止初始化。
(2)、Window/New命令的程式程式
当主框架窗口上有子窗口时,选取Window/New命令能够生成该聚会子窗口的影象。它们有相同的文档模板、相同的文档。其程式如下:
 
 
 
 
执行Window/New的过程与File/New的过程差不多。所不相同的是,File/New须要创建唯一新文档,而Window/New则是获得已存在的MDI子窗口的文档。因此以前存在的视和New以后生成的视均为该文档的视,都是该文档的内容的呈现。当调用CDocument::UpdateAllViews参数时,它们(视)的OnUpdate参数都将被激活。此时,在该文档的视指针列表中,将有多于唯一的视(具体数目视Window/New执行的次数而定)。读者能够利用(图3)中的代码Code跟踪程式结果。
 
(四)、几种情况的研讨
上面,笔者就MFC汉文档/视的联系停止了考虑,下面,笔者将结合具体情况停止研讨:
(1)、问号根据自个的要求来选取文档模板,及相应的视和文档。
在通常的MDI实际运用程式中,只有唯一文档模板,程式员只能打开一种类别的文档。因此,程式员只要调用File/New或者File/Open创建或者打开文档即可,至于文档、视和框架窗口之间的联系,由文档模板在幕后控制,不须要对文档模板停止操作。但是,假如实际运用程式需求处理多种类别的文档,并且何时打开何种文档均需程式员手工控制,此时,程式员必需对文档模板停止编程。
例如,笔者需求处理AVI和BMP两种文档类别。AVI和BMP的数据存放各式不相同,别用同一的数据框架来描述,因此,把它们的数据都存入唯一文档是不合适的。同时,由于AVI是图象序列,BMP仅是一幅图象,它们的呈现是肯定不相同的,即它门的视不相同。基于此,笔者决定分别建立两套文档模板,两套框架窗口,两套文档和两套视,分别用于AVI和BMP的数据存放和呈现。程式能够根据用户选取的文档名来分别处理AVI和BMP。具体步骤如下:
(Step 1)、在实际运用程式类(CWinApp)的派生类中渐增文档模板成员变量,以便对文档模板停止操作。
class C3dlcsApp : public CWinApp
{ 。。。 。。。
public:
CMultiDocTemplate * m_pAVIDocTemplate;
CMultiDocTemplate * m_pBMPDocTemplate;
}
(Step 2)、在主框架中渐增菜单响应:
void CMainFrame::OnFileOpen() {
CFileDialog my(true);
if(my.DoModal()==IDOK) {
CString FileName = my.GetPathName();
CString FileExt = my.GetFileExt();
if((FileExt == "AVI") || (FileExt == "avi")) {
CMyApp * pMyApp = (CMyApp *)AfxGetApp();
CMultiDocTemplate*pAVIDocTemplate=pMyApp->m_pAVIDocTemplate;
pAVIDocTemplate->OpenDocumentFile(FileName);
}
else if((FileExt == "BMP") || (FileExt == "bmp")) {
CMyApp * p3dlcsApp = (CMyApp *)AfxGetApp();
CMultiDocTemplate* pDATDocTemplate=pMyApp->m_pBMPDocTemplate;
pDATDocTemplate->OpenDocumentFile(FileName);
}
else {
AfxMessageBox("Yor select a file not supported!");
return;
}
}
}
笔者把用户输入文档名的后缀作为分支要求,假如是AVI文档,则先获得涉及AVI文档的文档模板,然后调用CDocTemplate::OpenUpdateFrame (lpszFileName)参数打开此文档。正如前面所考虑,此参数将依次生成新文档,新框架,在CMDIChildWnd::OnCreateClient中创建视,最后向框架中所有的视发送初始化消息,使其呈现在屏幕上。假如是BMP文档,操作类似。
当然,程式员也能够在程式的任何位置呈现此操作:经过全局参数AfxGetApp 获得实际运用程式对象指针,从而获得相应的文档模板指针。
由于由AppWizard生成的实际运用程式会缺省调用CWinApp::OnFileNew,所以当程式开端执行时,会在主框架上呈现唯一新的空窗口。假如想去掉那个空窗口,只须重载CWinApp::OnFileNew参数,不许要任何代码Code,即可。
 
(2)、切分窗口与文档/视框架
唯一文档能够有多个视,切分窗口即是表明多视的一种窍门。 切分窗口是经过类CSplitterWnd来表明的,对Window来说,CSplitterWnd对象是唯一真正的窗口,它完全占据了框架窗口的客户区域,而视窗口则占据了切分窗口的窗片区域。切分窗口并不参加命令传递机制,(窗片中)聚会的视窗从逻辑上来看直接被连到了它的框架窗口中。
切分窗口能够分为动态和静态两种。前者较简单,本文仅研讨后者。创建切分窗口的步骤如下:
(Step 1)、在自个的框架窗口中声明成员变量,用以对切分窗口停止操作。
class CMyFrame : public CMDIChildWnd
{ 。。。 。。。
CSplitterWnd m_Splitter;
CSplitterWnd m_Splitter2;
}
(Step 2)、重载CMDIChildWnd::OnCreateClient参数,创建切分窗口。
BOOL CMyFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{
BOOL btn = m_Splitter.CreateStatic(this,1,2);
btn |= m_Splitter.CreateView(0,0, RUNTIME_CLASS(CAVIDispView), CSize(100,100), pContext);
m_Splitter2.CreateStatic(&m_Splitter,
2, 1,
WS_CHILD | WS_VISIBLE | WS_BORDER,
m_Splitter.IdFromRowCol(0, 1));
btn |= m_Splitter2.CreateView(0, 0, RUNTIME_CLASS(CBMPView),
CSize(100,100), pContext);
btn |= m_Splitter2.CreateView(1, 0, RUNTIME_CLASS(CAVIView),
CSize(100,100), pContext);
return btn;
//return CMDIChildWnd::OnCreateClient(lpcs, pContext);
}
CFrameWnd::OnCreateClient参数原形为:
virtual BOOL OnCreateClient(LPCREATESTRUCT lpcs, CcreateContext * pContext);
缺省的CMDIChildWnd::OnCreateClient参数根据pContext参数parameter帮助的消息,调用CFrameWnd::CreateView参数创建唯一视。能够重载该参数,加载CCreateContext对象中传递的值,或改变框架窗口主客户区中控制的创建方法。在上面的程式中,笔者 创建了3个切分窗口。比如打开了唯一名为“a.avi”的文档,此时该文档将有3个视,唯一框架窗口。假如执行了Window/New操作,则此时有唯一文档,6个视和2个框架窗口。若该文档调用CDocument::UpdateAllViews参数,则这6个视的CView::OnUpdate参数都会被激发。
(3)、涉及CCreateContext的研讨。
CCreateContext是MFC框架所应用的一种框架,它将构成文档/视的组件联系起来。那个框架包括指向文档的指针,框架窗口,视以及文档模板,它还包含唯一指向CRuntimeClass的指针,以指明所创建的视的类别。其数据成员如下:
m_pNewViewClass:指向创建上下文的视的CRuntimeClass的指针。
m_pCurrentDoc:指向文档对象的指针,以和新视联系起来。
m_pNewDocTemplate:指向与框架窗口的创建相联系文档模板的指针。
m_pLastView:指向已存在的视,它是新产生的视的模型。
m_pCurrentFrame:指向已存在的框架窗口,它是新产生的框架窗口的模型。
程式员能够经过改变CCreateContext对象的值,来创建更加灵活的视。由于过程较复杂,笔者不再赘许叙,读者可参阅有联系的Visual C++ Help文档。
(五)、结束语
Visual C++ 6.0的文档/视框架代表了一种新的程式设计方法,其核心是文档与视的分离,即数据存放与呈现(操作)的分离。在MFC类库中,各个对象之间的联系很复杂,但,只要深入了解后,会发明它们之间是相互联系的,能够相互存取的。假如大部份人想设计出灵活、健壮的实际运用程式,就必需深入了解MFC。跟踪原代码Code就唯一较好的窍门。文档/视的联系的确非比寻常复杂,假如能明白每个参数是在哪调用的,执行了何种操作,就能游人刃有余,写出优美的实际运用程式。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值