一 切分窗口
1 类型
动态切分-程序在运行时,由用户拖动分隔条动态的切分窗口。
每一个视图窗口使用的是相同的视图类。
静态切分-在编码创建时已经完成窗口切分。每一个视图窗口
可以使用不同的视图类。
2 相关类
CSplitterWnd类-完成窗口切分的类。
#include <afxext.h>//扩展窗口的头文件
3 使用
3.1 动态切分
3.1.1 在CMainFrame中定义切分窗口对象
3.1.2 通过使用CCreateContext结构指定使用的视图类
3.1.3 创建动态切分
CSplitterWnd::Create
注意:切分数量不能超过2*2
3.2 静态切分
3.1.1 在CMainFrame中定义切分窗口对象
3.1.2 在CMainFrame::OnCreateClient()函数中,完成切分
CFrameWnd::OnCreateClient-虚函数,会被
CFrameWnd::OnCreate调用,作用创建框架窗口的客户
区对象。
1 创建静态切分
CSplitterWnd::CreateStatic
2 创建视图
CSplitterWnd::CreateView
3.1.3 切分窗口的再切分
1再切分对象的父窗口是前一个切分对象,而不是主窗口
2设置在切分对象的ID
CSplitterWnd::IdFromRowCol
3 创建视图
4 获取指定视图
CSplitterWnd::GetPane
5 设置/获取当前活动视图
CSplitterWnd::SetActivePane/GetActivePane
6 设置分隔条的位置(水平/垂直)
CSplitterWnd::SetRowInfo
CSplitterWnd::SetColumnInfo
无论是动态切分还是静态切分,切分出来的每一个视图窗口都是
独立的。
二 文档类
1 相关类
CDocument类-继承自CCmdTarget类,意味着文档类也可以处理
命令消息。
2 使用(在视图类中显示文档类的数据)
2.1 相关函数
2.1.1 CView::OnInitialUpdate()-初始化视图对象
被框架窗口的函数调用,在第一次附加文档之后,视图窗口
初始化显示之前。
2.1.2 CView::GetDocument()
获取与视图关联的文档
2.1.3 CFrameWnd::InitialUpdateFrame()-初始化更新框架
在框架窗口创建后调用该函数,引起该框架内所有的视图窗口
的OnInitialUpdate()函数的调用。
2.2 实现步骤:
2.2.1 在App::InitInstance()函数中:
1 创建框架对象 new CMainFrame
2 定义结构体变量 CCreateContext cxt;保存视图类的
运行时类信息的地址,创建文档,保存文档对象地址。
3 调用LoadFrame函数,创建框架窗口,并把变量cxt
作为该函数的参数。
4 作为应用程序主窗口,显示和更新
2.2.2 在文档中添加成员变量m_strData,并初始化
2.2.3 重写CView::OnInitialUpdate()函数,在函数中,
把文档中的数据显示到视图。
2.2.4 在LoadFrame函数之后,调用了InitialUpdateFrame()
函数。
2.3 思考问题:
1 视图对象是在什么时候被动态创建?
2 文档与视图的关系什么?关系什么时候建立?
2.3.1 在CFrameWnd::OnCreate()函数中:
OnCreate->OnCreateHelper->OnCreateClient
->CreateView(),CreateView()函数的执行流程:
//1 动态创建视图对象
m_pNewViewClass->CreateObject(); 587行
//2 创建视图窗口
pView->Create(...); 597行
2.3.2 在CView::OnCreate()函数中:(viewcore.cpp)
调用m_pCurrentDoc->AddView(this);
AddView()函数的执行流程:
void CDocument::AddView(CView* pView)
{
...
//将视图的地址保存到文档对象的链表中
m_viewList.AddTail(pView);
//将文档地址保存到视图对象的变量中
pView->m_pDocument = this;
...
}
视图类与文档类相互保存对方的地址。一个文档可以对应
多个视图,但是一个视图只能对应一个文档。
2.4 对命令消息的相应顺序
ActiveView->Document->Frame->App
2.4.1 CFrameWnd::OnCmdMsg
2.4.2 CView::OnCmdMsg
顺序可以重写OnCmdMsg()函数修改,但是通常不去修改。
三 单文档视图架构程序
mvc架构-m:model,模型,管理和保存数据。
v: view , 视图,显示数据
c: controller, 控制器 ,接收和分发命令。
m: Document,文档
v: View, 视图
c: Frame, 框架
1 相关类
CWinApp/CFrameWnd/CView/CDocument
文档模板类-统一的方式创建框架、视图、文档对象。
CDocTemplate-抽象基类,提供了所有文档模板的功能。
CSingleDocTemplate-单文档模板类
CSingleDocTemplate(
UINT nIDResource,//资源ID
CRuntimeClass* pDocClass,//文档类的运行时类信息
CRuntimeClass* pFrameClass,//框架类的运行时类信息
CRuntimeClass* pViewClass//视图类的运行时类信息
);
注意:定义文档、框架和视图类时需要动态创建的支持
--------------------------------------------------
一 单文档视图架构应用程序
1 概念
在某一时刻只能管理一个文档
2 实现过程
2.1 AddDocTemplate()函数
void CWinApp::AddDocTemplate(CDocTemplate* pTemplate)
{
if (m_pDocManager == NULL)
m_pDocManager = new CDocManager;
m_pDocManager->AddDocTemplate(pTemplate);
}
void CDocManager::AddDocTemplate(CDocTemplate* pTemplate)
{
...
//在文档模板链表中添加新建的文档模板
m_templateList.AddTail(pTemplate); 534行
}
结论:
1 应用程序没有直接管理模板,而是交给文档管理类管理模板
2 在文档管理类使用链表保存文档模板,可以管理多个模板
2.2 OnFileNew()函数
void CWinApp::OnFileNew()
{
if (m_pDocManager != NULL)
m_pDocManager->OnFileNew();
}
void CDocManager::OnFileNew()
{
...
//获取保存的文档模板
CDocTemplate* pTemplate =
(CDocTemplate*)m_templateList.GetHead();
//调用文档模板的OpenDocumentFile()函数
pTemplate->OpenDocumentFile(NULL); 827行
}
CDocument* CSingleDocTemplate::OpenDocumentFile(...)
{
//创建新的文档对象
pDocument = CreateNewDocument(); 112行
//创建新的框架对象和框架窗口
pFrame = CreateNewFrame(pDocument, NULL); 132行
}
CFrameWnd* CDocTemplate::CreateNewFrame(...)
{
//创建新的框架对象
CFrameWnd* pFrame = 264行
(CFrameWnd*)m_pFrameClass->CreateObject();
//创建新的框架窗口
if (!pFrame->LoadFrame(m_nIDResource,...); 277行
}
调用LoadFrame,产生WM_CREATE消息,在消息处理函数
OnCreate()中,调用CreateView()函数,创建视图对象和
视图窗口。
创建视图窗口时,产生WM_CREATE消息,在消息处理函数
OnCreate()中,调用AddView()函数,文档与视图相互保存
对方的地址。
3 类与类(对象与对象)之间的关系
CWinApp
|->m_pDocManager(CDocManager*)
|->m_templateList (CSingleDocTemplate*)
|->m_pOnlyDoc (CDocument*)
|->m_pMainWnd(CFrameWnd*)
|->m_pActiveView(CView*)
|->m_pDocument (CDocument*)
|->m_viewList (CView*)
结论:
MFC各个类之间的关系通过包含对方地址产生的。
4 对命令消息的相应顺序
View->Document->Frame-App
二 多文档视图应用程序
1 概念
同时管理多个文档
2 相关类
CWinApp
CMDIFrameWnd-多文档的主框架类(1个对象)
CMDIChildWnd-多文档的子框架类(多个对象)
CView/CDocument-视图/文档(多个对象)
CMultiDocTemplate-多文档模板类
CMultiDocTemplate(
UINT nIDResource,//*子框架窗口的资源ID
CRuntimeClass* pDocClass,//文档类运行时类信息
CRuntimeClass* pFrameClass,//*子框架的运行时类信息
CRuntimeClass* pViewClass //视图类的运行时类信息
);
多文档中主框架窗口和子框架窗口分别拥有自己的菜单和图标。
主框架的对象以及窗口自己创建,而子框架、文档和视图使用
模板创建。
多文档中主框架窗口的菜单项至少有两项
3 创建(多个视图数据同步的例子)
3.1
"新建视图"菜单
-只创建子框架和视图窗口,文档不创建,使用当前的活动视图
对应的文档。一个文档对应多个视图窗口。
"新建"菜单
-执行OnFileNew()函数,文档、子框架和视图都会被创建。
3.2
ON_CONTROL_REFLECT(EN_CHAGE,OnEnChange)//消息映射宏,
捕捉到视图的内容发生变化的消息。在消息处理函数中:
3.2.1 把活动视图的内容保存到文档
3.2.2 通知其它视图从文档获取最新数据
3.3
重写CView::OnUpdate()函数,在函数中,获取文档数据,显示
到当前视图。
4 类之间的关系与单文档类似...
三 MFC绘图
1 相关类
1.1 绘图设备类
CDC-绘图设备类的父类,封装的是一般的绘图设备,例如:
打印机、显示器等。
CWindowDC-父类是CDC类,封装的是指定窗口。包括窗口
客户区和非客户区。
CClientDC-父类是CDC类,封装的是指定窗口的客户区。
CPaintDC-父类是CDC类,封装的是指定窗口的客户区。该类
只能用在WM_PAINT消息处理函数中。
CMetaFileDC-父类是CDC类,保存绘制的命令。可以在需要
的时候,重新执行这些绘制命令。
1.2 绘图对象类
2 使用
2.1 CDC类的使用
2.1.1 创建
virtual BOOL CreateDC(
LPCTSTR lpszDriverName,//设备驱动的名称
LPCTSTR lpszDeviceName,//设备名称
LPCTSTR lpszOutput, //NULL
const void* lpInitData//初始化参数
);
如果描述的设备是显示器,("DISPLAY",NULL,NULL,NULL);
2.1.2 绘图或者输出文本
例如:TextOut()函数,输出文本
2.1.3 删除
CDC::DeleteDC()
2.2 CMetaFileDC类的使用
2.2.1 创建
CMetaFileDC::Create
2.2.2 绘制
...
2.2.3 关闭,返回句柄 HMETAFILE
CMetaFileDC::Close
2.2.4 重新执行绘制命令
CDC::PlayMetaFile(HMETAFILE )
2.2.5 删除
DeleteMetaFile(HMETAFILE)
-----------------------------
一 MFC绘图
1 绘图设备类
2 绘图对象类
CGdiObject类-所有绘图对象的父类,抽象父类,不会直接使用
CPen类-画笔
CBrush类-画刷
CFont类-字体
CBitmap类-位图
CRgn类-区域(可以进行计算的区域)
CPalette类-调色板
3 使用
3.1 画笔、画刷和字体的使用步骤
3.1.1 创建或者构造绘图对象
3.1.2 将绘图对象选入到当前设备
3.1.3 使用绘图对象
3.1.4 恢复到默认的绘图对象
3.1.5 删除绘图对象
3.2 位图的使用步骤
3.2.1 创建与当前窗口dc的兼容dc
CDC::CreateCompatibleDC
3.2.2 加载位图(将位图对象与位图资源建立关联)
CBitmap::LoadBitmap
3.2.3 将位图对象选入到兼容dc中
CDC::SelectObject
3.2.4 将位图从兼容dc拷贝到当前dc
CDC::BitBlt
CDC::StretchBlt-可以拉伸或者压缩图片
3.2.5 绘图兼容dc的默认位图对象
3.2.6 删除兼容dc和位图对象
使用该功能,可以被视图添加背景图片
3.3 CRgn类的使用步骤
3.3.1 创建几何区域
CRgn::CreateXXX
3.3.2 将两个几何区域进行运算的到复杂的几何区域
CRgn::CombineRgn
注意:运算可以进行多次
3.3.3 填充几何区域
CDC::FillRgn
3.3.4 填空几何区域的边框
CDC::FrameRgn
应用:
CWnd::SetWindowRgn-设置不规则的窗口形状
CPalette类-调色板,作用降低位图占用的空间大小。
RGB(0~255,0~255,0~255),24位真彩色。
800*600的位图,真彩色位图:800*600*3个字节
调色板:3*48 48色位图:800*600*1(颜色表中的索引值)+3*48
二 MFC绘图的例子
1 分析
1.1 图形包括 直线、矩形和椭圆,所需数据是起点和终点。
1.2 处理的消息:
1.2.1 LBUTTONDOWN-记录图形的起点坐标,开始画线
1.2.2 MOUSEMOVE-判断如果开始画线,擦除原来的线
(用与屏幕相同的颜色在相同位置画线),画出新的线。
1.2.3 LBUTTONUP-画线结束。
2 实现
2.1 绘图如何解决屏幕闪烁?
办法:1 使用双缓冲区绘图 2 局部刷新代替全部刷新
2.2 OpenGL库,Direct Draw
三 MFC的文件操作
1 相关类
CFile类-封装了文件句柄和相关操作的win32 api。
CFileFind类-文件查找。
2 CFile类的使用
2.1 打开或者新建文件
CFile::Open
2.2 文件读写
CFile::Read/Write
注意:通常放到异常处理结构中。读写操作时,注意当前
的文件指针。
2.3 关闭文件
CFile::Close
2.4 获取/设置文件的状态信息(属性)
CFile::GetStatus/SetStatus
3 CFileFind类的使用
3.1 开始查找,返回值代表是否找到文件
CFileFind::FindFile
3.2 查找一下,(获取第一个文件信息,返回下一个文件是否存在)
CFileFind::FindNextFile
3.3 获取和判断文件信息
CFileFind::GetXXX和IsXXX
3.4 关闭查找
CFileFind::Close
作业:
1 使用CFileFind类查找并输出C盘根目录下的文件和目录
2 使用CFileFind类查找并输出C盘下所有的文件和目录
------------------------------------------
一 序列化
1 概念
以二进制流的方式将数据依次写入到文件或者从文件中读取
2 相关类
CFile类
CArchive类-提供具体读写文件的操作,代替CFile::Read/Write
优点:1 可以设置缓冲区大小 2 读写各种数据类型更方便。
3 使用
3.1 打开或者新建文件
3.2 文件读写操作
3.2.1 定义CArchive类的对象
3.2.2 使用">>","<<"进行文件读写
3.2.3 关闭CArchive对象
3.3 关闭文件
二 对象的序列化(MFC的第6个机制),有的书上称为串行化
1 概念
序列化对象(至少需要运行时类信息的支持)
以二进制流的方式将对象的类的信息以及对象的成员变量依次写入
到文件的过程,称为序列化对象。
反序列化对象(至少需要动态创建的支持)
以二进制的方式从文件中读取类的信息创建对象,然后依次读取
保存的值初始化新创建的对象。这个过程称为反序列化对象。
2 实现
2.1 定义支持序列化的类
2.1.1 派生自CObject类
2.1.2 添加序列化的声明和实现宏
2.1.3 重写CObject::Serialize 函数,在函数中,将类的
成员变量序列化。
2.2 使用
对象的序列化的过程与一般变量类似,注意在读写时,">>"
"<<"函数的参数是对象的地址。
3 实现原理
3.1 展开宏
3.1.1 一个动态创建宏和一个友元函数
3.1.2 结构体类型的变量_init_CStudent的作用是
将当前类的运行时类信息的地址保存到应用程序的链表中
struct AFX_CLASSINIT
{
//构造函数
AFX_CLASSINIT(CRuntimeClass* pNewClass)
{
AfxClassInit(pNewClass);
}
};
void AFXAPI AfxClassInit(CRuntimeClass* pNewClass)
{
//获取应用程序的模块状态信息
AFX_MODULE_STATE* pModuleState =
AfxGetModuleState();
AfxLockGlobals(CRIT_RUNTIMECLASSLIST);
//将当前类的运行时类信息的地址保存到应用程序的链表中
pModuleState->m_classList.AddHead(pNewClass);
AfxUnlockGlobals(CRIT_RUNTIMECLASSLIST);
}
3.2 序列化对象的过程
CArchive& AFXAPI operator<<(CArchive& ar, const CObject* pOb)
{
ar.WriteObject(pOb);
return ar;
}
ar.WriteObject(pOb)
{
//1 获取当前类的运行时类信息的地址
CRuntimeClass* pClassRef = pOb->GetRuntimeClass();
//2 将类的版本号、类名称长度和类名写入文件
WriteClass(pClassRef);
//3 将类的成员变量姓名和年龄依次写入到文件
((CObject*)pOb)->Serialize(*this)
{
CObject::Serialize(ar);
if (ar.IsStoring())
{
ar<<m_strName<<m_nAge;
}
else
{
ar>>m_strName>>m_nAge;
}
}
}
WriteClass(pClassRef)
{
pClassRef->Store(*this); 252行
}
pClassRef->Store(*this)
{
//获取类名称字符串长度
WORD nLen = (WORD)lstrlenA(m_lpszClassName);
//首先写入类的版本和类名称长度
ar << (WORD)m_wSchema << nLen;
//其次写入类名称字符串
ar.Write(m_lpszClassName, nLen*sizeof(char));
}
3.3 反序列化对象的过程
CArchive& AFXAPI operator>>(CArchive& ar, CStudent* &pOb)
{
pOb = (CStudent*)
ar.ReadObject(RUNTIME_CLASS(CStudent));
return ar;
}
ar.ReadObject(RUNTIME_CLASS(CStudent))
{
//1 从文件中读取类名,遍历链表找到运行时信息
CRuntimeClass* pClassRef = ReadClass(...); 125行
//2 使用运行时类信息的变量创建对象
pOb = pClassRef->CreateObject(); 150行
//3 从文件中读取成员变量初始化新建的对象
pOb->Serialize(*this); 161行
{
...
}
}
CRuntimeClass* pClassRef = ReadClass(...)
{
pClassRef = CRuntimeClass::Load(); 301行
}
CRuntimeClass::Load(...)
{
//依次从文件中,读取类的版本、类名称长度和类名
ar >> wTemp; *pwSchemaNum = wTemp;
ar >> nLen;
if (nLen >= _countof(szClassName) ||
ar.Read(szClassName, nLen*sizeof(char)) != nLen*sizeof(char))
{
return NULL;
}
//将类名保存在szClassName这个数组中
szClassName[nLen] = ' ';
//获取应用程序的模块状态信息
AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
AfxLockGlobals(CRIT_RUNTIMECLASSLIST);
//遍历模块状态信息中保存各个类的运行时类信息变量地址的链表
for (pClass = pModuleState->m_classList; pClass != NULL;
pClass = pClass->m_pNextClass)
{
比较类的名称,如果在链表中找到,则返回变量的地址
if (lstrcmpA(szClassName, pClass->m_lpszClassName) == 0)
{
AfxUnlockGlobals(CRIT_RUNTIMECLASSLIST);
return pClass;
}
}
}
三 MFC对话框
1 分类
模式和非模式
2 相关类
CDialog类-父类是CWnd,本质上是一个窗口,拥有自己的资源,
方便的拖放控件。所有对话框类的父类。
CCommonDialog类-通用对话框类,包括文件对话框、颜色对话框
字体对话框、查找替换对话框、打印和打印设置对话框。
CPropertyPage类-属性页对话框。
3 在Win32工程中使用MFC的类创建基于对话框的应用程序
3.1 模式对话框
3.1.1 创建和显示
CDialog::DoModal()
3.1.2 关闭
CDialog::OnOK/OnCancel
3.1.3 对话框的初始化
CDialog::OnInitDialog
3.2 非模式对话框
3.2.1 创建和显示
创建和显示与一般的框架窗口过程相似
3.2.2 关闭
非模式对话框的关闭需要程序员处理
1 重写CDialog::OnOK和OnCancel这两个虚函数,在
函数中,DestroyWindow()
2 重写CWnd::PostNcDestroy()虚函数。在函数中,
delete this;
3.2.3 对话框的初始化
CDialog::OnInitDialog
思考:为什么模式对话框不需要程序员去销毁?
提示: 跟一下DoModal()函数的执行过程