VC随笔文档视图结构_单文档应用程序的框架窗口是唯一的主框架窗口,它的窗口类是cmain-程序员宅基地

技术标签: visual c++  文档  活动  化工  磁盘  mfc  框架  

VC随笔文档视图结构

使用断点调试观察MFC应用程序框架的创建过程:

由于一个对象被创建时会自动调用构造函数,而一个窗口被创建时会调用OnCreate函数,因此在下列地方设置断点:

1、应用程序类、文档类、框架窗口类、视图类的构造函数开始处(函数开始大括号所在行)

2CMy2DCADApp::InitInstance函数开始处、添加文档模板处、结尾处(函数结束大括号所在行)

3CMainFrame::OnCreate函数开始处、结尾处

设置完断点后,按F5键,程序进入调试状态,可见在编辑窗口中,程序首先停止在应用程序类CMy2DCADApp的构造函数中,说明程序最初构造的是应用程序类对象。

再按一次F5,编辑窗口中的黄色箭头停留在CMy2DCADApp::InitInstance函数的开始处,表明此时MFC框架中的WinMain函数开始执行,并调用了应用程序类的InitInstance函数。

再按一次F5,编辑窗口中的黄色箭头停留在InitInstance函数中添加文档模板对象处。此时文档模板对象被创建。

再按一次F5,编辑窗口中的黄色箭头停留在文档类CMy2DCADDoc的构造函数开始处,表明开始创建文档对象。

再按一次F5,编辑窗口中的黄色箭头停留在框架窗口类CMainFrame的构造函数开始处,表明开始创建框架窗口类对象。

再按一次F5,编辑窗口中的黄色箭头停留在框架窗口类CMainFrame::OnCreate函数开始处,表明开始创建框架窗口。

再按一次F5,编辑窗口中的黄色箭头停留在视图类CMy2DCADView的构造函数开始处,表明开始创建视图类对象。

再按一次F5,编辑窗口中的黄色箭头停留在CMainFrame::OnCreate函数结尾处,表明视图窗口和框架窗口都已经创建完毕。

再按一次F5,编辑窗口中的黄色箭头停留在CMy2DCADApp::InitInstance函数的结尾处,表明程序初始化完毕。

再按一次F5,此时出现2DCAD应用程序窗口界面,表明程序已经进入了主消息循环中,等待用户的输入。关闭2DCAD程序就可以结束调试。

 

几个使用最频繁的类的访问方法:

1、获取应用程序类对象指针

使用全局函数AfxGetApp()可以在任何时候获取到当前应用程序实例的指针,返回值是一个指向应用程序类的基类CWinApp的指针,在C++中可以用一个父类的指针指向派生类的对象,在需要时可以强制转换回来。

CWinApp* AfxGetApp();

2、获取主框架窗口类对象指针

1)首先获取应用程序对象,然后访问应用程序对象的公有成员变量m_pMainWnd

CMainFrame * pMainFrame;

pMainFrame = (CMainFrame *)AfxGetApp()->m_pMainWnd;

由于m_pMainWnd被声明为CWnd类型,因此需要强制转换。同时,还需要包含相应的类的头文件。

2)使用全局函数AfxGetMainWnd,该函数原型为:CWnd* AfxGetMainWnd();

由于返回值是CWnd*类型,因此,同样需要强制转换,代码如下:

CMainFrame * pMainFrame;

pMainFrame = (CMainFrame *)AfxGetMainWnd();

3、获取活动视图类对象指针

使用CFramWnd::GetActiveView函数获取当前活动视图的指针。该函数原型如下:

CView* GetActiveView() const;

返回值是指向视图基类CView的指针。使用时需要强制转换。

由于GetActiveViewCFramWnd的成员函数,因此,在获取视图对象指针之前,必须获取该视图窗口的外框架窗口的指针。

1)对于单文档应用程序来说,主框架窗口CMainFrame就是视图窗口的外框架窗口,因此,代码如下:

CMainFrame * pMainFrame;

pMainFrame = (CMainFrame *)AfxGetApp()->m_pMainWnd;//先获取框架窗口指针

CMy2DCADView * pView;

pView = (CMy2DCADView *)pMainFrame->GetActiveView();//再获取视图窗口指针

2)对于多文档应用程序,由于子窗口才是视图的外框架窗口,因此首先要用GetActiveFrame函数取得活动子框架窗口,然后通过该子窗口获取活动视图,代码如下:

CMainFrame * pMainFrame;

pMainFrame = (CMainFram *)AfxGetApp()->m_pMainWnd;//先获取框架窗口指针

CChildFrame * pChildFrame;

pChildFrame = (CChildFrame *)pMainFrame->GetActiveFrame;//再获取子框架窗口指针

CMutView * pView;

pView = (CMutView *)pMainFrame->GetActiveView();//最后获取视图窗口指针

注意:在代码所在的CPP文件中,必须包含所使用到的类的头文件。比如这里就需要包含4个类的头文件:MainFrm.hChildFrm.hMutDoc.hMutView.h。因为除了代码中的3个类之外,在MutView.h中还使用到了文档类。

4、获取活动文档类对象指针

1)在获取到活动视图之后,调用视图类的成员函数GetDocument获取视图所属的文档

2)获取了文档的外边框窗口后,使用CFrameWnd::GetActiveDocument函数,该函数的原型如下:

virtual CDocument* GetActiveDocument();

对于单文档应用程序,代码如下:

CMainFrame * pMainFrame;

pMainFrame = (CMainFrame *)AfxGetApp()->m_pMainFrame;//先获取框架窗口指针

CMy2DCADDoc * pDoc;

pDoc = (CMy2DCADDoc *)pMainFrame->GetActiveDocument();//再获取文档指针

对于多文档应用程序,代码如下:

CMainFram * pMainFrame;

pMainFrame = (CMainFrame *)AfxGetApp()->m_pMainFrame;//先获取主框架窗口指针

CChildFrame * pChildFrame;

pChildFrame = (CChildFrame *)pMainFrame->GetActiveFrame();//再获取子框架窗口指针

CMutDoc * pDoc;

pDoc = (CMutDoc *)pMainFrame->GetActiveDocument();//最后获取文档指针

同样,在代码所在的CPP文件中,必须包含所使用到的类的头文件。

 

文档的序列化(保存绘图结果):

首先将输入的图形用成与变量保存起来,然后写入磁盘文件中。

1、添加文档类成员变量

1)为文档类CMy2DCADDoc添加4个公有成员变量:

 int m_nPointNum;  //记录总点数

 int m_Point[500][2]; //记录各个点的坐标,最多允许500个点

 int m_nLineNum;   //记录总线段数

 int m_Line[500][4];  //记录线段端点坐标,最多允许500条线段

注意:为了简单起见,使用了固定大小的数组,实际运用中应该使用动态分配内存的方法。

2)在文档类的构造函数CMy2DCADDoc中添加初始化代码:

 m_nPointNum = 0; //初始点数为0
 m_nLineNum = 0;  //
初始线段数目为0

注意:要保存到文档中的数据成员必须放到文档中;一般的,与显示相关的数据成员都可以放在视图类中。总之,如何使用更方便,就如何分配数据成员。

2、在交换中修改文档数据

1)在画点画线时,在负责交互的视图类中改变文档类中的数据成员,记录下用户的输入。因此,在OnLButtonDown函数中的相应处添加修改文档数据的代码。

函数开始处获取文档指针:

 CMy2DCADDoc* pDoc = GetDocument(); //获取文档指针

在画点代码中修改文档中的记录点的数据成员:

 //
 //  
以下的代码在视图区中画点
  //
 //
 if(m_bIsPoint)//
如果程序处于画点状态

 {
  //
调用CDC::Ellipse函数以点为中心,画一个小的圆圈
  dc.Ellipse(point.x-2, point.y-2, point.x+2, point.y+2);

  //修改文档中的记录点的数据成员
  int i = pDoc->m_nPointNum;
  pDoc->m_Point[i][0] = point.x; //
记录点的坐标值
  pDoc->m_Point[i][1] = point.y;

  pDoc->m_nPointNum++; //点数加1
 }

在画线代码中修改文档中的记录点的数据成员:

  else//如果是线段终点,则在视图区中画出线段
  {
   dc.MoveTo(m_nStartX, m_nStartY);
   dc.LineTo(point.x, point.y);//
使用MoveToLineTo函数画线

   //修改文档中的记录点的数据成员
   int i = pDoc->m_nLineNum;
   pDoc->m_Line[i][0] = m_nStartX; //
记录线段起点坐标
   pDoc->m_Line[i][1] = m_nStartY;
   pDoc->m_Line[i][2] = point.x; //
记录线段终点坐标
   pDoc->m_Line[i][3] = point.y;

   pDoc->m_nLineNum++;   //线段总数加1

   m_nStep = 0;//完成一次画线段后,将操作步数归零
   m_nStartX = m_nStartY = 0;//
将记录起点坐标归零

   m_nEndX = m_nEndY = 0;//将终点坐标归零

   ::ReleaseCapture();//释放鼠标捕捉
  }

2)在视图类的OnDraw函数中添加重画代码,如下:

void CMy2DCADView::OnDraw(CDC* pDC)
{
 CMy2DCADDoc* pDoc = GetDocument();
 ASSERT_VALID(pDoc);
 // TODO: add draw code for native data here
 /
 //
重新绘制所有点

 for(int i=0; i<pDoc->m_nPointNum; i++)
 {
  int x = pDoc->m_Point[i][0];
  int y = pDoc->m_Point[i][1];

  pDC->Ellipse(x-2, y-2, x+2, y+2);
 }

 //
 //
重新绘制所有线段

 for(i=0; i<pDoc->m_nLineNum; i++)
 {
  int x1 = pDoc->m_Line[i][0];
  int y1 = pDoc->m_Line[i][1];
  int x2 = pDoc->m_Line[i][2];
  int y2 = pDoc->m_Line[i][3];

  pDC->MoveTo(x1, y1);
  pDC->LineTo(x2, y2);
 }
}

3、将文档类成员变量写入磁盘文件

编辑文档类的Serialize函数如下:

void CMy2DCADDoc::Serialize(CArchive& ar)
{
 if (ar.IsStoring())
 {
  // TODO: add storing code here
  ar<<m_nPointNum; //
首先存入总的点数

  for(int i=0; i<m_nPointNum; i++)
  {      //
使用循环存入各个点的坐标值
   ar<<m_Point[i][0]<<m_Point[i][1];
  }

  ar<<m_nLineNum;  //存入线段数目
  for(i=0; i<m_nLineNum; i++)
  {      //
使用循环存入各条线段端点的坐标值
   ar<<m_Line[i][0]<<m_Line[i][1]
    <<m_Line[i][2]<<m_Line[i][3];
  }
 }
 else
 {
  // TODO: add loading code here
  ar>>m_nPointNum; //
首先读出总的点数
  for(int i=0; i<m_nPointNum; i++)
  {      //
使用循环读出各个点的坐标值
   ar>>m_Point[i][0]>>m_Point[i][1];
  }

  ar>>m_nLineNum;  //读出线段数目
  for(i=0; i<m_nLineNum; i++)
  {      //
使用循环读出各条线段端点的坐标值
   ar>>m_Line[i][0]>>m_Line[i][1]
    >>m_Line[i][2]>>m_Line[i][3];
  }
 }
}

注意:(1)读入过程的顺序必须和存盘过程的顺序一致。(2CArchive类的“>>”“<<”操作符并不支持所有的标志数据类型。它支持的数据类型有:CObjectByteWORDintLongDWordFloatDouble。其他类型的数据要进行序列化输入输出时,需要将该类型的数据转化为上述几种类型之一方可。

 

文档的标志:

MFC应用程序框架中,文档类有一个成员变量m_bModified专门用来标记文档是否被修改过了,即文档的标志。

可以使用CDocument::IsModified函数来检查文档是否被修改过。

BOOL IsModified();

如果返回值为TRUE,表明文档被修改了;如果返回FALSE,表明文档已经存过盘或者没有被修改。

使用CDocument::SetModifiedFlag函数可以设置文档的标志。

void SetModifiedFlag(BOOL bModified = TRUE);

当用户存储文件的时候,应用程序框架会自动清除文档的标志。

2DCAD程序中要做的便是每当文档数据改变时,应该调用SetModifiedFlag函数来设置文档被修改的标志。在视图类中的两处(画点和画线)修改文档数据代码的后面添加如下语句:

  pDoc->SetModifiedFlag(); //设置文档标志

 

文档的初始化以及清楚:

1、初始化文档类的数据成员

一般来说,类的数据成员的初始化都是在构造函数中完成的,在构造函数调用结束时对象才真正存在。但对于文档来说却有所不同,文档类的数据成员的初始化工作应该在OnNewDocument成员函数中完成,此时文档对象已经存在。因为在用户选择File|New菜单项时,应用程序对象并不是销毁原来的文档对象然后重建新的文档对象,而只是重新初始化文档对象的数据成员,这个初始化工作就是由应用程序对象的OnFileNew消息处理成员函数通过调用OnNewDocument函数来完成的。

2DCAD程序的OnNewDocument函数中添加如下的代码:

BOOL CMy2DCADDoc::OnNewDocument()
{
 if (!CDocument::OnNewDocument())
  return FALSE;

 // TODO: add reinitialization code here
 // (SDI documents will reuse this document)
 m_nPointNum = 0; //
初始化点数目为
0
 m_nLineNum = 0;  //
初始化线段数目为0

 return TRUE;
}

2、清理文档类的数据成员

同文档的初始化一样,文档的清理也不是在文档的析构函数中完成的,而是在文档的CDocument::DeleteContents成员函数中完成的。默认的DeleteContents函数什么也不做。因此需要重写DeleteContents函数,并编写相应的文档清理代码。

1)启动ClassWizard,选择Message Maps选项卡。在Class Name下拉列表框中选择文档类CMy2DCADDoc,在ObjectIDs列表框选择CMy2DCADDoc,此时可以在Message列表框中找到DeleteContents项。双击该项,添加DeleteContents函数到文档类中。

2)单击Edit Code按钮,编辑DeleteContents函数如下:

void CMy2DCADDoc::DeleteContents()
{
 // TODO: Add your specialized code here and/or call the base class
 m_nPointNum = 0; //
清除文档中的数据

 m_nLineNum = 0;  //
清除文档中的数据

 CDocument::DeleteContents();
}

注意:这里初始化代码和删除代码是一样的,这只是一个巧合,二者的代码是根据具体情况而定的。比如使用动态内存分配的时候,文档中动态申请的内存就必须在DeleteContents函数中释放。

 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/keepAction/article/details/7853660

智能推荐

JWT(Json Web Token)实现无状态登录_无状态token登录-程序员宅基地

文章浏览阅读685次。1.1.什么是有状态?有状态服务,即服务端需要记录每次会话的客户端信息,从而识别客户端身份,根据用户身份进行请求的处理,典型的设计如tomcat中的session。例如登录:用户登录后,我们把登录者的信息保存在服务端session中,并且给用户一个cookie值,记录对应的session。然后下次请求,用户携带cookie值来,我们就能识别到对应session,从而找到用户的信息。缺点是什么?服务端保存大量数据,增加服务端压力 服务端保存用户状态,无法进行水平扩展 客户端请求依赖服务.._无状态token登录

SDUT OJ逆置正整数-程序员宅基地

文章浏览阅读293次。SDUT OnlineJudge#include<iostream>using namespace std;int main(){int a,b,c,d;cin>>a;b=a%10;c=a/10%10;d=a/100%10;int key[3];key[0]=b;key[1]=c;key[2]=d;for(int i = 0;i<3;i++){ if(key[i]!=0) { cout<<key[i.

年终奖盲区_年终奖盲区表-程序员宅基地

文章浏览阅读2.2k次。年终奖采用的平均每月的收入来评定缴税级数的,速算扣除数也按照月份计算出来,但是最终减去的也是一个月的速算扣除数。为什么这么做呢,这样的收的税更多啊,年终也是一个月的收入,凭什么减去12*速算扣除数了?这个霸道(不要脸)的说法,我们只能合理避免的这些跨级的区域了,那具体是那些区域呢?可以参考下面的表格:年终奖一列标红的一对便是盲区的上下线,发放年终奖的数额一定一定要避免这个区域,不然公司多花了钱..._年终奖盲区表

matlab 提取struct结构体中某个字段所有变量的值_matlab读取struct类型数据中的值-程序员宅基地

文章浏览阅读7.5k次,点赞5次,收藏19次。matlab结构体struct字段变量值提取_matlab读取struct类型数据中的值

Android fragment的用法_android reader fragment-程序员宅基地

文章浏览阅读4.8k次。1,什么情况下使用fragment通常用来作为一个activity的用户界面的一部分例如, 一个新闻应用可以在屏幕左侧使用一个fragment来展示一个文章的列表,然后在屏幕右侧使用另一个fragment来展示一篇文章 – 2个fragment并排显示在相同的一个activity中,并且每一个fragment拥有它自己的一套生命周期回调方法,并且处理它们自己的用户输_android reader fragment

FFT of waveIn audio signals-程序员宅基地

文章浏览阅读2.8k次。FFT of waveIn audio signalsBy Aqiruse An article on using the Fast Fourier Transform on audio signals. IntroductionThe Fast Fourier Transform (FFT) allows users to view the spectrum content of _fft of wavein audio signals

随便推点

Awesome Mac:收集的非常全面好用的Mac应用程序、软件以及工具_awesomemac-程序员宅基地

文章浏览阅读5.9k次。https://jaywcjlove.github.io/awesome-mac/ 这个仓库主要是收集非常好用的Mac应用程序、软件以及工具,主要面向开发者和设计师。有这个想法是因为我最近发了一篇较为火爆的涨粉儿微信公众号文章《工具武装的前端开发工程师》,于是建了这么一个仓库,持续更新作为补充,搜集更多好用的软件工具。请Star、Pull Request或者使劲搓它 issu_awesomemac

java前端技术---jquery基础详解_简介java中jquery技术-程序员宅基地

文章浏览阅读616次。一.jquery简介 jQuery是一个快速的,简洁的javaScript库,使用户能更方便地处理HTML documents、events、实现动画效果,并且方便地为网站提供AJAX交互 jQuery 的功能概括1、html 的元素选取2、html的元素操作3、html dom遍历和修改4、js特效和动画效果5、css操作6、html事件操作7、ajax_简介java中jquery技术

Ant Design Table换滚动条的样式_ant design ::-webkit-scrollbar-corner-程序员宅基地

文章浏览阅读1.6w次,点赞5次,收藏19次。我修改的是表格的固定列滚动而产生的滚动条引用Table的组件的css文件中加入下面的样式:.ant-table-body{ &amp;amp;::-webkit-scrollbar { height: 5px; } &amp;amp;::-webkit-scrollbar-thumb { border-radius: 5px; -webkit-box..._ant design ::-webkit-scrollbar-corner

javaWeb毕设分享 健身俱乐部会员管理系统【源码+论文】-程序员宅基地

文章浏览阅读269次。基于JSP的健身俱乐部会员管理系统项目分享:见文末!

论文开题报告怎么写?_开题报告研究难点-程序员宅基地

文章浏览阅读1.8k次,点赞2次,收藏15次。同学们,是不是又到了一年一度写开题报告的时候呀?是不是还在为不知道论文的开题报告怎么写而苦恼?Take it easy!我带着倾尽我所有开题报告写作经验总结出来的最强保姆级开题报告解说来啦,一定让你脱胎换骨,顺利拿下开题报告这个高塔,你确定还不赶快点赞收藏学起来吗?_开题报告研究难点

原生JS 与 VUE获取父级、子级、兄弟节点的方法 及一些DOM对象的获取_获取子节点的路径 vue-程序员宅基地

文章浏览阅读6k次,点赞4次,收藏17次。原生先获取对象var a = document.getElementById("dom");vue先添加ref <div class="" ref="divBox">获取对象let a = this.$refs.divBox获取父、子、兄弟节点方法var b = a.childNodes; 获取a的全部子节点 var c = a.parentNode; 获取a的父节点var d = a.nextSbiling; 获取a的下一个兄弟节点 var e = a.previ_获取子节点的路径 vue

推荐文章

热门文章

相关标签