1. 文档/视图模式简介:
a. 该模式的宗旨就是将数据操作和数据呈现在物理和逻辑上实现分离;
b. 文档只负责数据的存储和操作,而视图则负责将文档中的数据以图形化的形式显示给用户看,比如对话框、列表、各种框等等,显示的形式可以是多种多样的;
c. 文档/视图的对应关系:
i. 文档是视图的材料和基础;
ii. 因此文档对视图是一种一对多的关系;
iii. 即一个文档可以对应多个视图对象,而一个视图对象只能对应一个文档对象;
d. 视图类的三大用途:
i. 将数据从文档中取出,并且通过各种各样的形式呈现给用户;
ii. 为用户对数据的修改等提供一个图形界面,用户可在这些界面上描述对数据的操作;
iii. 视图再将这些界面上用户对数据的操作回馈给对应的文档类对象,在存储介质上对数据的真正的操作还是交由文档类对象执行;
2. CView类简介:
a. 即视图类,是创建的工程中CTestView类的基类;
b. WM_PAINT消息:
i. 是Windows窗口的一种重要消息,该消息可触发对窗口进行重画的操作;
ii. 向指定窗口发送WM_PAINT消息的时机:
*1. 第一次创建(即打开)窗口时;
*2. 窗口大小发生改变时,比如最大化、最小化窗口、利用下拉箭头等的拖动改变窗口大小比例;
*3. 把目标窗口从另一个窗口背后移出来时;
iii. 产生WM_PAINT消息的方法:
*1. 使用Invalide函数:该函数的作用就是告诉窗口,你里面的内容已经无效了,需要重新绘制,比如一个窗口被前台窗口遮住了,那么遮住部分将无效,因此当用户将该窗口移出时,系统就会调用该函数以发送WM_PAINT消息,触发重绘操作;
*2. 使用UpdateWindow函数:就是直接提示目标窗口,让其立即重绘,说到底作用和Invalide函数一模一样,只不过对他们取不同的名字可以在逻辑上更加清晰而已,而其唯一的区别就是Invalide函数是需要排队的,而UpdateWindow函数可以直接插队,说得明白点就是在同一时间段中,不仅仅只会产生WM_PAINT一种消息,很有可能产生多种消息,但是WM_PAINT消息是所有消息中优先级最低的,Invalide函数是将WM_PAINT消息插在消息队列的尾部,而UpdateWindow则是直接将WM_PAINT消息直接插在消息队列的头部,可以使得窗口立即实施重绘操作;
*3. 使用UpdateAll函数:调用该函数可以向一个文档的所有视图对象发送WM_PAINT消息,而上面的两个函数都只能向一个窗口发送WM_PAINT消息,该函数一般是在文档数据发生大规模改变时会被调用;
c. 实施重绘:
i. 即相应WM_PAINT消息;
ii. 通过OnPaint函数相应:
CView::OnPaint(void)
{
CPaintDC dc(this);//创建一个显示(即画图)设备对象与具体的画图设备关联
OnPrepareDC(&dc);//对该设备对象初始化(即准备)
OnDraw(&dc);//使用OnDraw函数,利用相应的显示设备显示(画出)图像
}
iii. 可以看出OnPaint函数只是OnDraw函数的设配器,并且为OnDraw函数准备画画的工具(作用就和画笔一样,只不过这里的是某台具体的显示设备在程序中的一种抽象,被抽象成了一种对象,就像所有硬件设备在操作系统中都被抽象成了文件一样);
iv. 因此真正的重绘工作需要交给OnDraw函数实施,一下是一个例子:
*1. 先在Doc类中创建一个成员变量m_showStr,然后在OnDraw函数中显示文档中的该数据,添加方法是在类视图中的Doc类上右键,点击“添加成员变量”,然后在对话框中输入名字和类型即可;
*2. 一下是Serialize和OnDraw函数的实现:
void CTestDoc::Serialize(CArchive& ar)
{
if (ar.IsStoring())
{
// TODO: add storing code here
}
else
{
// TODO: add loading code here
int i;
char ch;
double f;
CString str;
ar >> i >> ch >> f >> str;
m_showStr.Format("%d, %c, %f, %s", i, ch, f, str);
}
}
void CTestView::OnDraw(CDC* pDC)//CDC就是显示设备抽象成的类,pDC就是该显示设备对象的指针
{
CTestDoc* pDoc = GetDocument();//由于要绘的数据存在Doc文档中,因此要先获得文档对象的指针才能调出其中的数据
//这里要获得的数据就是m_showStr,即pDoc->m_showStr
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
pDC->TextOut(200, 200, pDoc->m_showStr);//使用绘图设备对象的TextOut函数,将字符串显示在屏幕的(200,200)坐标处
}
*3. 调试时点击打开按钮打开上一节中创建的文件,即可在绘图区的相应位置显示出这些信息了;
3. 总结在给类中获得其它类指针的方法:
a. 视图类中获得文档类的方法:GetDocument();
b. 文档类中获得视图类中的方法:
i. 由于一个文档可以对应多个视图,因此在文档中获得视图指针将会是多个;
ii. 先用GetFirstViewPosition()获得第一个视图的指针,当然该函数返回的是视图类对象的POSITION;
iii. 再使用while循环,并在循环判断中调用GetNextView函数,其原型为:CView *GetNextView( POSITION &rPosition )
传入当前视图的POSITION然后返回当前视图的指针,同时将实参输出为下一张视图的POSITION,如果下一张视图不存在(即视图已经遍历完毕),将返回NULL;
注:如果文档中的数据发生变化,则需要调用Doc类的成员函数UpdateAllViews函数通知各个视图更新其中的数据以和文档数据适配,当然在UpdateAllViews函数作用与各个视图后,各个视图将会调用格子相应的函数根据文档中的数据进行更新;
c. 框架类中获得当前处于激活状态(以后简称为当前的)视图所对应的文档的指针:
i. CMainFrame类派生于CFrameWnd类,其继承了里面的虚函数GetActiveDocument();
ii. 通过该函数可以获取当前视图所对应的文档的指针,返回类型是CDocument *;