一 切分窗口
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] = '\0'; //获取应用程序的模块状态信息 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()函数的执行过程