标题：CView
简介
从文档类中将文档中的数据取出后显示给用户；接受用户对文档中数据的编辑和修改；将修改的结果反馈给文档类，由文档类将修改后的内容保存到磁盘文件中。
文档负责了数据真正在永久介质中的存储和读取工作，视图呈现只是将文档中的数据以某种形式向用户呈现，因此一个文档可对应多个视图。
Invalidate(TRUE)将整个窗口设置为需要重绘的无效区域，它会产生WM_PAINT消息，这样OnDraw将被调用。
假如文档中的数据发生了变化，必须通知所有链接到该文档的视图，这时候文档类的UpdateAllView函数需要被调用。
然而，除了打印和打印预览外，视图类显示得很简单。
关联文档对象
前面已经提到，视图需要表示文档数据，所以文档对象与视图对象必须建立关联。这样，当文档数据发生变化时，它可以及时通知视图；当视图需要显示不同的文档数据时，它可以从文档对象中提取。
在文档/视图框架程序中，文档对象总是在视图之前建立，而在视图的WM_CREATE消息处理函数中，建立了它与文档对象的关联。代码如下：
Int CView::OnCreate (LPCREATESTRUCT lpcs)
{
if (CWnd::OnCreate(lpcs) == -1)
return -1;
// m_pDocuemnt视图成员，用于存储相应文档对象的指针，此时为空
ASSERT(m_pDocument == NULL);
CCreateContext* pContext = (CCreateContext*)lpcs->lpCreateParams;
// pContext->m_pCurrentDoc是已经建立的文档对象的指针
if (pContext != NULL && pContext->m_pCurrentDoc != NULL)
{
// 将当前视图加入文档对象的视图列表中，因为一个文档可关联多个视图
pContext->m_pCurrentDoc->AddView(this);
/* 在文档的AddView()函数中，已经将当前文档对象指针赋给m_pDocument视图成员。这样，在视图建立之初，二者就建立了关联。*/
ASSERT(m_pDocument != NULL);
}
else
{
TRACE(“Warning: Creating a pane with no CDocument.\n”);
}
return 0; // ok
}
同时，视图类定义了成员函数GetDocument()，返回文档对象的指针。代码如下：
CDocument* CView::GetDocument() const
{
ASSERT(this != NULL);
return m_pDocument;
}
视图总是在文档对象之前销毁，在视图的析构函数中，与文档对象解除关联。代码如下：
CView::~CView()
{
if (m_pDocument != NULL)
m_pDocument->RemoveView(this);
}
视图的绘制
窗口的绘制工作总是在WM_PAINT消息处理中进行的，当窗口需要绘制时，它会收到系统发来的WM_PAINT消息。在绘制过程中，首先要准备显示设备句柄，最后要释放句柄。众所周知，在视图窗口中绘制，无需重载WM_PAINT消息处理函数OnPaint()，因为有一个更加友好的绘制新口：OnDraw()。该函数的实参是一个已经准备好的显示设备，最后无需释放。其实，这一切还是WM_PAINT消息处理函数OnPaint()为我们准备的，代码如下：
void CView::OnPaint()
{
// CPaintDC类维护显示设备
CPaintDC dc(this);
OnPrepareDC(&dc);
OnDraw(&dc);
}
OnPrepareDC()是一个虚拟函数，它总是在OnDraw()之前执行，可以重载它，设置绘图模式。因为OnDraw()每次执行，都使用不同的CPaintDC对象，所以本次绘图模式的状态不能保留到下一次。
OnDraw()在视图基类CView中定义为纯虚函数。例如：
virtual void OnDraw(CDC* pDC) = 0;
所以CView是抽象基类，不能实例化，而派生类必须重载OnDraw()。
虚函数
OnUpdate当文档数据发生变化时，文档对象调用CDocument::UpdateAllView()通知所有视图，作为响应，视图的OnUpdate()成员被调用。所以，重载的OnUpdate()应该能够根据需要，将文档数据的变化反映在视图中。CView::OnUpdate()只是简单地使客户区无效，导致客户区重画。例如：
void CView::OnUpdate(CView* pSender, LPARAM, CObject*)
{
ASSERT(pSender != this);
UNUSED(pSender); // unused in release builds
Invalidate(TRUE);OnInitialUpdate()在初始创建、调用OnCreate()之后，或者在File/New、File/Open命令后被框架调用。基类CView::OnInitialUpdate()只是简单地调用OnUpdate()，可以重载它完成初始化工作。但注意，它可能被多次调用。
void CView::OnInitialUpdate()
{
OnUpdate(NULL, 0, NULL);
}CalcWindowRect
每当主框架窗口的客户区尺寸发生变化或控制条的位置发生变化，需要重新排列客户区时，调用该函数，根据视图客户区尺寸计算视图窗口的尺寸。
我们知道，排列主窗口客户区是由CFrameWnd::RecalcLayout()完成的。显然，视图的CalcWindowRect()函数也是由它触发调用的。主窗口的客户区尺寸减掉所有控制占用的部分，剩下的区域分给视图，这部分区域作为实参传入CalcWindowRect()。在CalcWindowRect()函数内，需要计算视图窗口的尺寸。代码如下：
void CView::CalcWindowRect(LPRECT lpClientRect, UNIT nAdjustType)
{
// lpClientRect此时是整个视图客户区的尺寸
// 需要为滚动条增加尺寸吗
if (nAdjustType != 0)
{
// 调用API，根据窗口风格计算窗口尺寸
::AdjustWindowRectEx(lpClientRect, 0, FALSE, GetExStyle());
DWORD dwStyle = GetStyle();
if (dwStyle & WS_VSCROLL)
{
// 为垂直滚动条增加尺寸
int nAdjust = afxData.csVScroll;
if (dwStyle & WS_BORDER)
nAdjust -= CX_BORDER;
lpClientRect->right += nAdjust;
}
if (dwStyle & WS_HSCROLL)
{
// 为水平滚动条增加尺寸
int nAdjust = afxData.cyHScroll;
if (dwStyle & WS_BORDER)
nAdjust -= CY_BORDER;
lpClientRect->bottom += nAdjust;
}
return;
}
// 无需为滚动条增加尺寸，调用基类成员完成计算
CWnd::CalcWindowRect(lpClientRect, nAdjustType);
}PostNcDestroy在视图窗口关闭时最后调用的成员函数，它与CFrameWnd::PostNcDestroy完成相同的功能，即删除视图对象。代码如下：
void CView::PostNcDestroy()
{
delete this;
}
这样，可以不必关心视图的释放工作，即使它在堆中构造。OnCmdMsg在讨论CFrameWnd::OnCmdMsg()时已经了解过该函数，下面只给出它的代码：
BOOL CView::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)
{
// 首先查找自身的命令消息映射
if (CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
return TRUE;
// 如果视图本身没有处理该命令，将机会留给与其关联的文档对象
if (m_pDocument != NULL)
{
CPushRoutingView push(this);
return m_pDocument->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
}
return FALSE;
}OnActivateView当视图被激活为活动视图，或由活动转为非活动时，调用该函数通知视图。基类的实现只是设置该视图为焦点。代码如下：
void CView::OnActivateView(BOOL bActivate, CView* pActivateView, CView*)
{
UNUSED(pActivateView); // unused in release builds
if (bActivate) // 当前状态为活动吗
{
ASSERT(pActivateView == this);
// 如果其父窗口也是活动的，则设置焦点。（在MDI中，其父框架可能是非活动的）
if (IsTopParentActive())
SetFocus();
}
}
MFC中CView派生类
MFC提供了丰富的CView派生类，各种不同的派生类实现了对不同种类控件的支持，以为用户提供多元化的显示界面。
CScrollView: 提供滚动支持；
CCtrlView: 支持tree、list和rich edit控件；
CDaoRecordView: 在dialog-box控件中显示数据库记录；
CEditView: 提供了一个简单的多行文本编辑器视图；
CFormView: 包含dialog-box控件，可滚动，基于对话框模板资源；
CListView: 支持列表控件视图；
CRecordView: 在dialog-box控件中显示数据库记录；
CRichEditView: 支持富文本编辑的控件视图；
CTreeView: 支持重点在树控件的文档/视图结构的视图。