首页 C++笔记孙鑫VC学习笔记孙鑫VC学习笔记

C++笔记孙鑫VC学习笔记孙鑫VC学习笔记

举报
开通vip

C++笔记孙鑫VC学习笔记孙鑫VC学习笔记孙鑫VC++视频笔记列表(全) 简 介 第一课 Windows程序内部运行机制 第二课 MFC程序框架的剖析 第三课 MFC消息映射机制和DC的获取 第四课 简单字处理软件 第五课 菜单编程 第六课 对话框编程1 第七课 对话框编程2 第八课 MFC中各类指针的获取 第九课 应用程序外观修改 第十课 图形绘制与通用对话框 第十一课 图形保存和重绘 第十二课 文件操作 第十三课 文档和串行化 ...

C++笔记孙鑫VC学习笔记孙鑫VC学习笔记
孙鑫VC++视频笔记列表(全) 简 介 第一课 Windows程序内部运行机制 第二课 MFC程序框架的剖析 第三课 MFC消息映射机制和DC的获取 第四课 简单字处理软件 第五课 菜单编程 第六课 对话框编程1 第七课 对话框编程2 第八课 MFC中各类指针的获取 第九课 应用程序外观修改 第十课 图形绘制与通用对话框 第十一课 图形保存和重绘 第十二课 文件操作 第十三课 文档和串行化 第十四课 网络编程 第十五课 多线程和简单聊天室制作 第十六课 线程同步与异步套接字编程 第十七课 进程间通信 第十八课 ActiveX 控件 第十九课 动态链接库 第二十课 Hook钩子函数 第0章 简介 Lesson1:Windows程序运行原理及程序编写流程,窗口产生过程,句柄原理,消息队列,回调函数,窗口关闭与应用程序退出的工作关系,使用VC++的若干小技巧,stdcall与cdecl调用规范的比较,初学者常犯错误及注意事项。 Lesson2:C++经典语法与应用,类的编写与应用,构造与析构函数,函数的重载,类的继承,函数覆盖,基类与派生类的构造函数、析构函数先后调用顺序,如何在派生类构造函数中向基类的构造函数传递参数,this成员变量,类型转换的内幕,虚拟函数与多态性,引用和指针变量的区别与共同处。VC工程的编译原理与过程,将工程中不同的类拆分到不同的文件中,每一个类由一个.h和.cpp文件共同完成,头文件重复定义问题的解决,培养了学员良好的编程习惯,也为以后 分析 定性数据统计分析pdf销售业绩分析模板建筑结构震害分析销售进度分析表京东商城竞争战略分析 MFC AppWizard生成的工程奠定了良好基础。 Lesson3:讲述MFC AppWizard的原理与MFC程序框架的剖析。AppWizard是一个源代码生成工具,是计算机辅助程序 设计 领导形象设计圆作业设计ao工艺污水处理厂设计附属工程施工组织设计清扫机器人结构设计 工具,WinMain在MFC程序中是如何从源程序中被隐藏的,theApp全局变量是如何被分配的,MFC框架中的几个类的作用与相互关系,MFC框架窗口是如何产生和销毁的,对窗口类的PreCreateWidow和OnCreate两个函数的着重分析,Windows窗口与C++中的CWnd类的关系。 Lesson4:MFC消息映射机制的剖析,讲述如何运用ClassWizard,,理解发送给窗口的消息是如何被MFC框架通过窗口句柄映射表和消息映射表来用窗口类的函数进行响应的。掌握设备描述表及其封装类CDC的使用,CDC是如何与具体的设备发生关联的,融合具体的画图程序进行分析。如何设置封闭图形的填充刷子(位图画刷与透明画刷的使用)。 Lesson5:掌握CDC的文字处理程序的编写,如何产生自定义字体和自定义插入符,熟悉对CString类的使用。通过对卡拉OK程序的编写,讲解定时器的使用和DrawText函数的巧妙运用。讲解如何使用CDC的裁减功能。 Lesson6:菜单的工作原理及编写应用,菜单命令消息在MFC框架程序的几个类中的传递顺序和处理过程。标记菜单、缺省菜单的实现原理、图形菜单的实现及常犯错误的分析,GetSystemMetrics的应用,快捷弹出菜单的实现方式及其命令响应函数有效范围(与弹出菜单时所指定的父窗口有密切的关系,最底层的子窗口具有最优先的处理机会)。动态菜单的编写,如何让程序在运行时产生新的菜单项及如何手工为这些新产生的菜单命令安排处理函数,如何在顶层窗口中截获对菜单命令的处理,更进一步掌握CString类的应用。 Lesson7:对话框用户界面程序的编写,如何向对话框控件关联数据成员及其实现机理,如何利用对话框类的成员函数向控件发送消息和获取对话框控件的类指针,如何直接利用对话框控件类操纵对话框控件(发送消息和直接调用成员函数)。如何在程序运行时产生和销毁控件。对话框控件的几种操作方式的优劣比较分析。如何实现对话框的部分收缩和展开。如何让对话框上的文本框在程序启动后立即获得焦点,如何利用SetWindowLong改变窗口的回调函数,通过改变文本框的默认回车处理方式进行演示。实现多个输入文本框间通过回车逐一向下传递焦点的另一种巧妙 方法 快递客服问题件处理详细方法山木方法pdf计算方法pdf华与华方法下载八字理论方法下载 (用缺省按钮来处理)。 Lesson8:逃跑按钮的巧妙实现。如何制作属性页对话框和向导对话框,融合讲解组合框(如何调整组合框的大小)、列表框、单选按钮、复选按钮等常用对话框控件的多种使用方法。如何限制用户在不满足设定的条件时切换到其他属性页和向导页。 Lesson9:如何修改MFC AppWizard向导生成的框架程序的外观和大小,修改图标、光标、背景的三种方法。如何增加和删除工具栏按钮,如何给应用程序增加工具栏,如何显示和隐藏工具栏。定制状态栏,在状态栏中添加时钟显示,CTime类及其用法。在状态栏中添加进度条(主窗口产生后立即产生进度条的巧妙思想,不能在OnCreate函数中直接处理,要用到自定义消息的方法)。鼠标坐标显示,在CView中获取状态栏对象的几种方式。如何为应用程序添加启动画面。 Lesson10:图形的绘制,如何使用自定义画笔(颜色,线宽,线型)。如何为程序中添加选项菜单和选项设置对话框,如何使用 标准 excel标准偏差excel标准偏差函数exl标准差函数国标检验抽样标准表免费下载红头文件格式标准下载 颜色对话框,如何使用字体对话框,在选项对话框中实现预览功能。实现选项对话框和窗口类中的数据交换。如何改变对话框和控件的背景色,如何改变控件的文本颜色,对按钮控件的特殊处理。如何在窗口中显示一幅位图。 Lesson11:如何让CDC上输出的文字、图形具有保持功能,集合类CPtrArray的使用,CPaintDC与CClientDC的区别与应用,OnPaint与OnDraw在CView中的关系及实现内幕,滚动窗口的实现,坐标空间,映射方式,设备坐标与逻辑坐标的转换。元文件设备描述表的使用,如何利用兼容DC实现图形的保存和再现。 Lesson12:const char *与char * const的区别。C语言对文件读写的支持,FILE指针;文本文件和二进制文件的区别。用文本方式读写文件和以二进制方式读写文件的注意事项。C++对文件读写的支持,ofstream和ifstream的用法。Win32 SDK对文件读写的支持,CreateFile函数、WriteFile函数、ReadFile函数的使用;MFC对文件读写的支持,CFile类和CFileDialog的使用,文件过滤器的设置。win.ini文件和注册表的读写方式及相关 知识点 高中化学知识点免费下载体育概论知识点下载名人传知识点免费下载线性代数知识点汇总下载高中化学知识点免费下载 。 Lesson13:使用CArchive类对文件进行操作。MFC框架程序提供的文件新建与打开功能内部的实现机制。如何利用CDocument类的串行化存储功能保存与加载数据。如何实现类对串行化的支持,CObArray的串行化实现内幕。删除文档数据时常犯的错误。MFC框架程序的文档类和视类的关系,以及如何获得相互的指针引用。 Lesson14:网络的相关知识,网络程序的编写,Socket是连接应用程序与网络驱动程序的桥梁,Socket在应用程序中创建,通过bind与驱动程序建立关系。此后,应用程序送给Socket的数据,由Socket交给驱动程序向网络上发送出去。计算机从网络上收到与该Socket绑定的IP+Port相关的数据后,由驱动程序交给Socket,应用程序便可从该Socket中提取接收到的数据。网络应用程序就是这样通过socket进行数据的发送与接收的。TCP与UDP的工作原理与编写过程,如何在程序中链接库文件。一个字符界面的聊天程序。 Lesson15:多线程程序的编写,多线程应用中容易出现的问题。互斥对象的讲解,如何使用互斥对象来实现多线程的同步。如何利用命名互斥对象保证应用程序只有一个实例运行。应用多线程编写网络聊天室程序。 Lesson16:事件内核对象、关键代码段(临界区)的讲解,以及在多线程同步中的应用。在Windows下编写基于消息的网络应用程序,掌握阻塞与非阻塞网络程序的编写,理解在Windows平台下,采用异步选择机制可以提高网络应用程序的性能。 Lesson17:详细讲解进程间通讯的四种方式:剪贴板、匿名管道、命名管道和邮槽。并比较分析这几种进程间通信的优点和缺点。 Lesson18:ActiveX控件的应用与工作原理。ActiveX控件的编写,如何为控件安排属性,方法,事件,属性页,持久性存储,控件如何通知容器自身属性的改变。如何注册控件与取消控件注册。在VB和VC中访问ActiveX控件。 Lesson19:动态链接库程序的编写。静态库与动态库的区别,以及调用程序在链接静态库和动态库时的区别。如何利用工具查看动态链接库输出的函数,Depends工具的使用,C++编译器名字改编技术对动态链接库输出函数的影响,extern "C"的用法,利用模块定义文件来解决C++名字改编的问题。用typedef定义指向函数的指针类型,如何获得动态连接库里的函数的指针。 Lesson20:Hook编程。如何安装钩子过程,如何编写全局钩子,动态连接库里的全局变量数据共享问题分析。ADO数据库编程。在VB中利用ADO控件和ADO对象访问数据库,在VC中利用ADO技术访问数据库。 第一章 Windows程序内部运行机制 这一章比较零散,笔记也很零散 一、windows是事件驱动方式的程序设计 windows程序设计是种事件驱动方式的程序设计,主要基于消息的。当用户需要完成某种功能时,需要调用OS某种支持,然后OS将用户的需要包装成消息,并投入到消息队列中,最后应用程序从消息队列中取走消息并进行响应。 二、消息队列 每个应用程序OS都为它建立一个消息队列,消息队列是个先进先出的缓冲区,其中每个元素都是一个消息,OS将生成的每个消息按先后顺序放进消息队列中,应用程序总是取走当前消息队列中的第一条消息,应用程序取走消息后便知道用户的操作和程序的状态,然后对其处理即消息响应,消息响应通过编码实现。 三、Window程序入口 WinMain函数是Windows程序入口点函数,由OS调用,当OS启动应用程序的时候,winmain函数的参数由OS传递的。 四、创建一个完整的窗口需要经过下面四个操作步骤 1, 设计一个窗口类。如:WNDCLASS wndcls。 2, 注册窗口类。如:RegisterClass(&wndcls)。 3, 创建窗口。如:CreateWindow(),CreateWindowEX(); 4, 显示及更新窗口。如:ShowWindow(),UpdateWindow(); 其他值得注意的地方 1, 消息结构 typedef struct tagMSG { // msg HWND hwnd; //接收消息的窗口句柄。和哪个窗口相关联。 UINT message; //消息标识。消息本身是什么。 WPARAM wParam; //消息的附加信息。具体取决于消息本身。 LPARAM lParam; DWORD time; //消息投递时间。 POINT pt; //消息投递时,光标在屏幕上的位置。 } MSG; 2, 掌握windows的消息机制 需要掌握两方面:(1)消息本身。不同消息所代表的用户操作和应用程序的状态。 (2)对于某个特定的消息来说,要让OS执行某个特定的功能去响应消息。 3, Window入口函数说明 int WINAPI WinMain( HINSTANCE hInstance, // 当前事例句柄。 HINSTANCE hPrevInstance, // 先前事例句柄。32位系统中此值都为NULL LPSTR lpCmdLine, // 命令行指针 int nCmdShow // (窗口)显示的状态 ); 要带参调用WinMain,类似于命令行的功能,在ProjectèsettingèDebug页èProgram arguments项填写参数 4, Windows提供的窗口类详解 typedef struct _WNDCLASS { UINT style; //窗口的类型 WNDPROC lpfnWndProc; //窗口过程函数指针(回调函数) int cbClsExtra; //窗口类附加字节,为该类窗口所共享。通常0。 int cbWndExtra; //窗口附加字节。通常设为0。 HANDLE hInstance; //当前应用程序事例句柄。 HICON hIcon; //图标句柄 LoadIcon(); HCURSOR hCursor; //光标句柄 LoadCursor(); HBRUSH hbrBackground; //画刷句柄 (HBRUSH)GetStockObject(); LPCTSTR lpszMenuName; //菜单名字 LPCTSTR lpszClassName; //类的名字 } WNDCLASS; 5, 消息循环: MSG msg; while(GetMessage(&msg,...)) //从消息队列中取出一条消息 { TranslateMessage(&msg); //进行消息(如键盘消息)转换 DispatchMessage(&msg); //分派消息到窗口的回调函数处理,(OS调用窗口回调函数进行处理)。 } 6, 回调函数 LRESULT CALLBACK WindowProc( //这里WindowProc是个代号名字。 HWND hwnd, // handle to window UINT uMsg, // message identifier WPARAM wParam, // first message parameter LPARAM lParam // second message parameter ); 说明:两种函数调用约定(__stdcall 和 __cdecl): #define CALLBACK __stdcall //__stdcall 标准调用预定,是PASCAL 调用约定,象DELPHI使用的就是标准调用约定 #define WINAPIV __cdecl // __cdecl 是C 语言形式的调用约定。 主要区别:函数参数传递顺序 和 对堆栈的清除上。 问题:除了那些可变参数的函数调用外,其余的一般都是__stdcall约定。但 C/C++编译默然的是__cdecl约定。所以如果在VC等环境中调用__stdcall约定的函数,必须要在函数声明的时加上 __stdcall 修饰符,以便对这个函数的调用是使用__stdcall约定(如使用DELPHI编写的DLL时候)。 (VC中可通过这途径修改:projectèsettingsèc/c++ècategary选”Code Generation”,出现Call convention选项,修改之) 7, DC句柄的使用 (1)使用BeginPaint(),EndPaint()对。注意只能在响应WM_PAINT消息时使用。 (2)使用GetDc(),ReleaseDC()对。注意他们不能在响应WM_PAINT中使用。 8, 其它 (1)函数名代表函数的首地址。 (2)创建窗口的时候一定要基于已经注册的窗口类. 第二章 MFC程序框架的剖析 说明:本课对应教学视频的第三课,第二课是C++,被我跳过去。C++我打算另外在用多点的篇幅来讨论。 重点:MFC运行机制 提示:对于不想理解内部运行过程的,可以不看这一章,可以看了后面的界面设计再回头来看这一章,可能感觉更深刻。 这一次课和上一次的课的重点就是MFC的窗口类创建过程,而要反复说明的就是:MFC的程序和C语言的程序,从执行原理上说,是完全一致的。 抓住这一点,那么对于理解MFC程序的运行机制也就相对于简单了。 C中的main函数就相当于MFC中的WinMain函数。 感兴趣的可以利用VC的断点设置自己跟踪下面讲述的各个函数,就明白它的执行顺序了。 一、C语言程序执行步骤 在C语言中,大约的步骤如下: 1, 全局变量内存分配 2, 进入main函数 二、MFC程序的运行步骤(主要是初始化) 打开一个MFC APPWizard(exe)工程,跟踪其执行步骤,可以发现,是以下顺序: 1) CXXApp中的全局变量定义 CXXApp theApp; 2) 调用CXXApp构造函数 CXXApp ::CXXApp(){} 3) 进入Winmain函数(_tWinMain为宏,值为WinMain) _tWinMain(){} 4) 完成初始化工作:包括窗口类注册、窗口产生、显示和更新 pThread->InitInstance() 对于MFC程序,MainFrame,View,ToolBar,Controlbar等都是窗口,所以下面的窗口注册与创建、显示等要反复调用多次,一次对应一个窗口 (1) 注册窗口类 AfxEndDeferRegisterClass (2) 创建窗口 CMainFrame::PreCreateWindow()//反复调用一次是给我们修改窗口属性的机会 CFrameWnd::Create() (3) 消息循环 PumpMessage() 补充1: 在MFC中,由于涉及到(窗口)类定义,所以定义全局变量的时候,需要进行更多的步骤。 全局变量涉及到类定义(类似于C中的类型定义)的话,那么需要遵循以下步骤(以MFC的窗口类为例) 1) 设计一个窗口类 2) 注册窗口类 3) 创建窗口 4) 显示及更新窗口 5) 消息循环 补充2:其他需要注意的几点 1, 每一个MFC程序,有且只有一个从WinApp类派生的类(应用程序类),也只有一个从应用程序类所事例化的对象,表示应用程序本身。在WIN32程序当中,表示应用程序是通过WINMAIN入口函数来表示的(通过一个应用程序的一个事例号这一个标识来表示的)。在基于MFC应用程序中,是通过产生一个应用程序对象,用它来唯一的表示了应用程序。 2, _tWinMain函数中通过调用AfxWinMain()函数来完成它要完成的功能。(Afx*前缀代表这是应用程序框架函数,是一些全局函数,应用程序框架是一套辅助生成应用程序的框架模型,把一些类做一些有机的集成,我们可根据这些类函数来设计自己的应用程序)。 3, 设计窗口类:在MFC中事先设计好了几种缺省的窗口类,根据不同的应用程序的选择,调用AfxEndDeferRegisterClass()函数注册所选择的窗口类。 4, PreCreateWindow()是个虚函数,如果子类有则调用子类的。 5, CreateWindowEx()函数参数与CREATESTRUCT结构体成员完全一致,CreateWindowEx()函数与CREATESTRUCT结构体参数的对应关系,使我们在创建窗口之前通过可PreCreateWindow(cs)修改cs结构体成员来修改所要的窗口外观。 6,注意两个函数。 ::TranslateMessage(&m_msgCur)函数进行消息(如键盘消息)转换 ::DispatchMessage(&m_msgCur)函数分派消息到窗口的回调函数处理(实际上分派的消息经过消息映射,交由消息响应函数进行处理。) 7,可以认为View类窗口是CMainFram类窗口的子窗口。DOCument类是文档类。DOC-VIEW结构将数据本身与它的显示分离开。 文档类用于数据的存储,加载;视类用于数据的显示,修改 8,CTEApp::InitInstance()函数中通过文档模板将文档类,视类,框架类的有机组织一起。语句如下: CSingleDocTemplate* pDocTemplate; pDocTemplate = new CSingleDocTemplate( IDR_MAINFRAME, RUNTIME_CLASS(CTEDoc), RUNTIME_CLASS(CMainFrame), // main SDI frame window RUNTIME_CLASS(CTEView)); AddDocTemplate(pDocTemplate);//增加到模板 补充3:本课涉及到MFC函数的源文件位置 根目录 找到您安装VC98下MFC的位置,比如我的机子上为:D:\Program Files\Microsoft Visual Studio\VC98\MFC。下面提供的就是相对路径了。 CWinApp构造函数: MFCèSRCèAPPCORE.CPP AfxWinMain:MFCèSRCèWINMAIN.CPP AfxEndDeferRegisterClass: MFCèSRCèAPPCORE.CPP CFrameWnd::PreCreateWindow()函数所在文件:MFCèSRCèWINFRM.CPP CFrameWnd::Create()函数路径:MFCèSRCèWINFRM.CPP CWnd::CreateEx()函数路径:MFCèSRCèWINCORE.CPP CWinThread::Run()方法路径:MFCèSRCèTHRDCORE.CPP 第三章 MFC消息映射机制和绘图DC的获取 我们假设您已经: 1,知道如何创建一个单文档的App Wizard 2,知道C++ 类、函数重载等简单知识 3,知道如何给View类或者Doc文档添加成员变量 4,会用MFC的IDE调试工具最好,那么本文的程序您可以copy去调试 我们在上一节讲了MFC框架App类、View类、MainFrame类和Doc类的关系,那么,基于消息的windows MFC程序设计是如何进行消息映射的呢? 在进行下一节之前,我们来复习一下。 对于一个MFC APPWizard运用程序,CMainFrame和View是窗口类,并且是父子关系,Doc一般用于数据的加载和存储,View用于图像的显示,App中是包括一些(窗口)初始化之类的东西。 我们开始这一节的内容。 一、 消息映射机制 windows程序设计是种事件驱动方式的程序设计,主要基于消息的。当用户需要完成某种功能时,需要调用OS某种支持,然后OS将用户的需要包装成消息,并投入到消息队列中,最后应用程序从消息队列中取走消息并进行响应。 在左边View类处点右键,在出现的菜单里点击“Add Windows Message Handler”,在出现的对话框里选择“WM_LBUTTONDOWN”,添加消息映射函数。 回到原文件,我们将看到三处进行了修改: 1, 在头文件(View.h)中声明消息响应函数原型。 //{{AFX_MSG(CDrawView) //注释宏 afx_msg void OnLButtonDown(UINT nFlags, CPoint point); //}}AFX_MSG //注释宏 afx_msg宏表示声明的是一个消息响应函数。 2, 在源文件(View.cpp)中进行消息映射。 BEGIN_MESSAGE_MAP(CDrawView, CView) //{{AFX_MSG_MAP(CDrawView) ON_WM_LBUTTONDOWN() //}}AFX_MSG_MAP 在宏BEGIN_MESSAGE_MAP()与END_MESSAGE_MAP()之间进行消息映射。 宏ON_WM_LBUTTONDOWN()把消息WM_LBUTTONDOWN与它的响应函数OnLButtonDown()相关联。这样一旦有消息的产生,就会自动调用相关联的消息响应函数去处理。 宏ON_WM_LBUTTONDOWN()定义如下: #define ON_WM_LBUTTONDOWN() { WM_LBUTTONDOWN, 0, 0, 0, AfxSig_vwp, (AFX_PMSG)(AFX_PMSGW)(void (AFX_MSG_CALL CWnd::*)(UINT, CPoint))&OnLButtonDown }, 3, 源文件中进行消息响应函数处理。 void CDrawView::OnLButtonDown(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default CView::OnLButtonDown(nFlags, point); } 说明: 可见当增加一个消息响应处理,在以上三处进行了修改。可在消息响应函数里添加消息处理代码完成对消息的响应、处理。 消息响应的可能方式: 1)在基类中针对每种消息做一个虚函数,当子类对消息响应时候,只要在子类中重写这个虚函数即可。缺点:MFC类派生层次很多,如果在基类对每个消息进行虚函数处理,那么从基类派生的每个子类都将背负一个庞大的虚表,这样浪费内存,故MFC没有采取这中方式而采取消息映射方式。 2)消息映射方式:MFC在后台维护了一个句柄和C++对象指针对照表,当收到一个消息后,通过消息结构里资源句柄(查对照表)就可找到与它对应的一个C++对象指针,然后把这个指针传给基类,基类利用这个指针调用WindowProc()函数对消息进行处理,WindowProc()函数中调用OnWndMsg()函数,真正的消息路由及处理是由OnWndMsg()函数完成的。由于WindowProc()和OnWndMsg()都是虚函数,而且是用派生类对象指针调用的,由多态性知最总终调用子类的。在OnWndMsg()函数处理的时候,根据消息种类去查找消息映射,判断所发的消息有没有响应函数,具体方式是到相关的头文件和源文件中寻找消息响应函数声明(从注释宏//{{AFX_MSG(CDrawView)...//}}AFX_MSG之间寻找),消息映射(从宏BEGIN_MESSAGE_MAP(...)....END_MESSAGE_MAP()之间寻找),最终找到对应的消息处理函数。当然,如果子类中没有对消息进行处理,则消息交由基类处理。 说明: virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam); virtual BOOL OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult); 二、 绘图DC的获取 说明:在View类添加WM_LBUTTONDOWN和WM_LBUTTONUP的消息处理函数,我们将用来演示各种DC的获取,以及显示效果。 View类添加全局变量CPoint m_ptOrigin用来存储左键按下点坐标。下面集中来关注OnLButtonUp中的绘图程序和效果。 以下语句添加于OnLButtonUp函数中,可以查看不同的效果。 1,使用SDK获取DC句柄: HDC hdc; hdc=::GetDc(m_hWnd);//获取DC句柄 MoveToEx(hdc,m_ptOrigin.x,m_ptOrigin.y,NULL); LineTo(hdc,point.x,point.y); ::ReleaseDC(m_hWnd,hdc);//释放DC 2,利用CDC类指针和CWin类成员函数获取DC。 CDC *pDC=GetDC(); pDC->MoveTo(m_ptOrigin); pDC->LineTo(point); ReleaseDC(pDC); 3,利用CClientDC对象。(CClientDC类从CDC类派生来的) CClientDC dc(this); dc.MoveTo(m_ptOrigin); dc.LineTo(point); 4,利用CWindowDC对象。(CWindowDC类从CDC类派生来的) CWindowDC dc(this);// dc.MoveTo(m_ptOrigin); dc.LineTo(point); 5,父窗口(MainFrame框架)和屏幕指针。 将上面的dc(this)分别改成GetParent()和GetDesktopWindow(),就可以得到父窗口指针和屏幕窗口指针。 可以分别试验画线效果。 6,利用画笔改变线条颜色和类型: CPen pen(PS_DOT,1,RGB(0,255,0));//构造画笔对象 CClientDC dc(this);CPen *pOldPen=dc.SelectObject(&pen);//将画笔选入DC dc.MoveTo(m_ptOrigin); dc.LineTo(point); dc.SelectObject(pOldPen);//恢复先前的画笔 7,使用画刷(通常利用画刷去填充矩形区域): 使用单色画刷 CBrush brush(RGB(255,0,0));//构造画刷对象 CClientDC dc(this); dc.FillRect(CRect(m_ptOrigin,point),&brush);//用指定的画刷去填充矩形区域 使用位图画刷 CBitmap bitmap;//构造位图对象(使用前需要初试化) bitmap.LoadBitmap(IDB_BITMAP1);//初试化位图对象 CBrush brush(&bitmap);//构造位图画刷 CClientDC dc(this); dc.FillRect(CRect(m_ptOrigin,point),&brush);//用指定的位图画刷去填充矩形区域 使用透明画刷 CBrush *pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));//获取透明画刷对象指针 CClientDC dc(this); CBrush *pOldBrush=dc.SelectObject(pBrush);//将透明画刷选入DC dc.Rectangle(CRect(m_ptOrigin,point)); dc.SelectObject(pOldBrush);//释放透明画刷 8,注意点: 1)静态方法不属于某一个具体对象,而属于类本身,在类加载的时候就已经为类静态方法分配了代码去,故可用CBrush::FromHandle()形式调用。 2)静态方法中,不能引用非静态的数据成员和方法。 3)静态数据成员需要在类外单独做初始化,形式如: 变量类型 类名::变量名=初始值; 第四章 文本编辑和字处理软件 这一节我们主要讲述文本编辑方面的知识,孙鑫老师的视频就是一个简单字处理软件的制作过程,所以我也是按照顺序,叙述此过程。 在MFC中CEditView 和 cRichEditView类已经完成了初步的文字处理。可以让应用程序的View类以CEditView 和 cRichEditView类为基类。下面我们以单文档视图为例 一、创建插入符。文字在插入符后插入(正如Word中闪烁的光标) 1, 添加View类的WM_CREATE消息响应函数 2, 在CXXXView::OnCreate()中添加 //获得当前文本度量/字体信息 CClientDC dc(this); TEXTMETRIC tm; dc.GetTextMetrics(&tm); //根据当前字体,设置插入符/光标 CreateSolidCaret(20,100); //** ShowCaret(); 3, 创建图形插入符 1),为View类添加成员变量m_bmp; 2),把上面的**行用如下语句替换 m_bmp.LoadBitmap(IDB_BITMAP1); CreateCaret(&bitmap); 4, 二、 输出文字 在OnDraw函数中添加如下代码 1),简单输出文字 CString str; str="维新科学技术培训中心"; pDC->TextOut(50,50,str); 2),获取文字框大小备用 CSize sz=pDC->GetTextExtent(str); 3),给str重新赋值,以区别于上一个内容。当然,之前需要在String Table中添加IDS_WEIXIN str.LoadString(IDS_WEIXIN); pDC->TextOut(0,200,str); 4),添加路径层 当作图的时候,如果想要在整幅图形其中的某个部分和其它部分有所区别,我们可以把这部分图形放到路径层当中,然后指定调用指定互操作模式调用SelectClipPath( int nMode )函数来使路径层和覆盖在其上新绘图剪切区域进行互操作,达到特殊效果。 pDC->BeginPath(); pDC->Rectangle(50,50,50+sz.cx,50+sz.cy);//路径层的坐标依赖于上面的文字位置 pDC->EndPath(); pDC->SelectClipPath(RGN_DIFF); 5),画一些方格,看看路径层的效果。 for(int i=0;i<300;i+=10) { pDC->MoveTo(0,i);pDC->LineTo(300,i); pDC->MoveTo(i,0);pDC->LineTo(i,300); } 三、鼠标移动插入符 给View增加两个成员变量: CString m_strLine用于存储当前输入行的内容;CPoint m_ptOrigin用于保存鼠标移动引起插入符改变时的新行开始位置 在OnLButtonDown函数中添加如下内容 SetCaretPos(point);//设置插入符位置 m_strLine.Empty(); m_ptOrigin=point;//保存此位置 另外,给View添加两个变量 四、响应键盘输入 键盘响应需要考虑如下内容: 1, 鼠标点击键盘输入一个字符,显示到键盘上,插入符/光标后移一位。 2, 回车之后,光标下移一行 3, 删除/退格键的响应 下面是程序内容: 1,给View类添加WM_CHAR消息响应函数 2,在OnChar函数中添加如下语句 CClientDC dc(this); CFont font; font.CreatePointFont(300,"华文行楷",NULL); CFont *pOldFont=dc.SelectObject(&font); TEXTMETRIC tm; dc.GetTextMetrics(&tm); if(0x0d==nChar)//回车键响应程序段 { m_strLine.Empty(); m_ptOrigin.y+=tm.tmHeight; } else if(0x08==nChar) { //用背景色模拟最后一个字符的消失 COLORREF clr=dc.SetTextColor(dc.GetBkColor()); dc.TextOut(m_ptOrigin.x,m_ptOrigin.y,m_strLine); m_strLine=m_strLine.Left(m_strLine.GetLength()-1); dc.SetTextColor(clr); } else { m_strLine+=nChar; } CSize sz=dc.GetTextExtent(m_strLine); CPoint pt; pt.x=m_ptOrigin.x+sz.cx; pt.y=m_ptOrigin.y; SetCaretPos(pt); dc.TextOut(m_ptOrigin.x,m_ptOrigin.y,m_strLine); dc.SelectObject(pOldFont); 五、平滑变色---类似卡拉OK的跟唱文字颜色改变 CDC::TextOut()是一个字母一个字母的输出,达不到平滑效果。 CDC::DrawText():将文字的输出局限于一个矩形区域,超出矩形区域的文字都被截断。利用这一特点,可每隔些时间增加矩形大小,从而可实现人眼中的平滑效果。 1,设置定时器。 在View中OnCreate或者其他地方添加 SetTimer(1,100,NULL); 2,给View类添加WM_TIMER消息响应函数 3,给View添加成员变量m_nWidth用于保存颜色随时间流逝变化的起点 4,在OnTimer中添加如下代码 m_nWidth+=5;//每次位置增加 CClientDC dc(this); TEXTMETRIC tm; dc.GetTextMetrics(&tm);//获取字体属性 CRect rect(0,200, m_nWidth,200+ tm.tmHeight); dc.SetTextColor(RGB(255,0,0)); CString str; str.LoadString(IDS_WEIXIN); dc.DrawText(str,rect,DT_LEFT); rect.top=150; rect.bottom=rect.top+tm.tmHeight; dc.DrawText(str,rect,DT_RIGHT); CSize sz=dc.GetTextExtent(str); if(m_nWidth>sz.cx) { m_nWidth=0; dc.SetTextColor(RGB(0,255,0)); dc.TextOut(0,200,str); } 简单字处理软件大功告成! 第五章 VC菜单相关编程 阅读本文前,我们假设您已经: 1,知道如何创建一个单文档的App Wizard 2,知道C++ 类、函数重载等简单知识 3,知道如何给View类或者Doc文档添加成员变量 4,会用MFC的IDE调试工具最好,那么本文的程序您可以copy去调试 5,知道如何为某个框架类添加虚函数或消息处理函数 一、消息的分类: 消息的分类:标准消息,命令消息,通告消息。 [标准消息]:除WM_COMMAND之外,所有以WM_开头的消息。 [命令消息]:来自菜单、加速键或工具栏按钮的消息。这类消息都以WM_COMMAND呈现。 在MFC中,通过菜单项的标识(ID)来区分不同的命令消息;在SDK中,通过消息的wParam参数识别。 [通告消息]:由控件产生的消息,例如,按钮的单击,列表框的选择等均产生此类消息,为的是向其父窗口(通常是对话框)通知事件的发生。这类消息也是以WM_COMMAND形式呈现。 注意: 1)从CWnd派生的类,都可以接收到[标准消息]、[命令消息]和[通告消息]。 2)从CCmdTarget派生的类,都可以接收到[命令消息]和[通告消息]。 3)CCmdTarget是CWnd的父类 二、菜单消息传递过程 MFC中菜单项消息如果利用ClassWizard来对菜单项消息分别在上述四个类中进行响应,则菜单消息传递顺序:View类èDoc类èCMainFrame类èApp类。菜单消息一旦在其中一个类中响应则不再在其它类中查找响应函数。 当点击一个菜单项的时候,最先接受到菜单项消息的是CMainFrame框架类,CMainFrame框架类将会把菜单项消息交给它的子窗口View类,由View类首先进行处理;如果View类检测到没对该菜单项消息做响应,则View类把菜单项消息交由文档类Doc类进行处理;如果Doc类检测到Doc类中也没对该菜单项消息做响应,则Doc类又把该菜单项消息交还给View类,由View类再交还给CMainFrame类处理。如果CMainFrame类查看到CMainFrame类中也没对该消息做响应,则最终交给App类进行处理。 三、菜单指针的获取,及相关设置 在CMainFrame::OnCreate下可以直接实验以下操作 几个相关和重要的函数 CMenu* GetMenu( ) ;//CWnd::GetMenu得到窗口菜单栏对象指针。 CMenu* GetSubMenu( ) ;//CMenu::GetSubMenu获得指向弹出菜单对象指针 UINT CheckMenuItem( );//CMenu::CheckMenuItem Adds check marks to or removes check marks from menu items in the pop-up menu. BOOL SetDefaultItem();//CMenu::SetDefaultItem Sets the default menu item for the specified menu. BOOL SetMenuItemBitmaps( );//CMenu::SetMenuItemBitmaps 设置位图标题菜单。 UINT EnableMenuItem();//CMenu::EnableMenuItem使菜单项有效,无效,或变灰。 BOOL SetMenu( CMenu* pMenu );//CWnd::SetMenu在当前窗口上设置新菜单或移除菜单。 HMENU Detach( );//CMenu::Detach Detaches a Windows menu from a CMenu object and returns the handle. 获取菜单的宽和高: GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) 例子: 1, 给菜单项打上标记 GetMenu()->GetSubMenu(0)->CheckMenuItem(0,MF_BYPOSITION | MF_CHECKED); GetMenu()->GetSubMenu(0)->CheckMenuItem(ID_FILE_NEW,MF_BYCOMMAND | MF_CHECKED); 2, 设置缺省菜单项 GetMenu()->GetSubMenu(0)->SetDefaultItem(1,TRUE); GetMenu()->GetSubMenu(0)->SetDefaultItem(ID_FILE_OPEN); 3, 图形标记菜单 先创建图形,注意底色不要是白色 m_bitmap.LoadBitmap(IDB_BITMAP1); GetMenu()->GetSubMenu(0)->SetMenuItemBitmaps(0,MF_BYPOSITION,&m_bitmap,&m_bitmap); 4, 屏蔽菜单,使之不能用 (需要在CMainFrame::CMainFrame()中设置m_bAutoMenuEnable=FALSE;) GetMenu()->GetSubMenu(0)->EnableMenuItem(1,MF_BYPOSITION | MF_DISABLED | MF_GRAYED); 5, 取消和加载菜单 用此功能,可以动态的修改菜单 SetMenu(NULL);//取消菜单项 CMenu menu; menu.LoadMenu(IDR_MAINFRAME); SetMenu(&menu); menu.Detach();//菜单句柄和对象断开,使对象析构时不销毁菜单 四、命令更新机制 菜单项状态的维护是依赖于CN_UPDATE_COMMAND_UI消息,谁捕获CN_UPDATE_COMMAND_UI消息,MFC就在其中创建一个CCmdUI对象。 在后台操作系统发出WM_INITMENUPOPUP消息,然后由MFC的基类如CFrameWnd接管并创建一个CCmdUI对象和第一个菜单项相关联,调用对象成员函数DoUpdate()(注:这个函数在MSDN中没有找到说明)发出CN_UPDATE_COMMAND_UI消息,这条消息带有指向CCmdUI对象的指针。此后同一个CCmdUI对象又设置为与第二个菜单项相关联,这样顺序进行,直到完成所有菜单项。 更新命令UI处理程序仅应用于弹出式菜单项上的项目,不能应用于永久显示的顶级菜单项目。 注意:以下两语句的效果不同。对菜单项一样,对工具栏索引对应不一样 if(0==pCmdUI->m_nIndex)pCmdUI->Enable(FALSE); if(ID_FILE_NEW==pCmdUI->m_nID)pCmdUI->Enable(FALSE); 五、右键弹出菜单 1, Project->Add to Project->Components and Controls添加pop menu即可。 2, 静态添加菜单方法。 1) 在资源里编辑一个菜单 2) View中添加WM_RBUTTONDOWN消息对应函数。 3) 在OnRButtonDown中添加如下 CMenu menu; menu.LoadMenu(IDR_MENU1); CMenu *pPopup=menu.GetSubMenu(0); ClientToScreen(&point); pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y,GetParent());//不想在框架类显示就把GetParent()改为this。 4) 给自己编辑的菜单加对应的处理函数(利用Classwizard)。 如果加在CMainFrame 5) 3,动态添加菜单(子菜单数目变化) 在MainFrame::OnCreate中试验如下 CMenu menu; menu.CreatePopupMenu(); GetMenu()->AppendMenu(MF_POPUP,(UINT)menu.m_hMenu,"WinSun"); GetMenu()->InsertMenu(2,MF_BYPOSITION | MF_POPUP,(UINT)menu.m_hMenu,"WinSun"); menu.AppendMenu(MF_STRING,IDM_HELLO,"Hello"); menu.AppendMenu(MF_STRING,112,"Weixin"); menu.AppendMenu(MF_STRING,113,"Mybole"); menu.Detach(); 响应函数添加方法: 1) 在resource.h中添加资源ID定义 #define IDM_HELLO 111 2) 在MainFrm.h中声明消息响应。DECLARE_MESSAGE_MAP()之前添加 afx_msg void OnHello(); 3) 在MainFrm.cpp中END_MESSAGE_MAP()之前,添加 ON_COMMAND(IDM_HELLO,OnHello) 4) 在MainFrm.cpp中添加CMainFrame::OnHello()函数定义 4, 给系统菜单添加/删除菜单项 GetMenu()->GetSubMenu(0)->AppendMenu(MF_STRING,114,"Welcome"); GetMenu()->GetSubMenu(0)->InsertMenu(ID_FILE_OPEN, MF_BYCOMMAND | MF_STRING,115,"维新"); GetMenu()->DeleteMenu(1,MF_BYPOSITION); GetMenu()->GetSubMenu(0)->DeleteMenu(2,MF_BYPOSITION);*/ GetMenu()->DeleteMenu( GetMenu()->GetSubMenu(1)->GetMenuItemID(0),MF_BYCOMMAND); 六、动态添加系统菜单项 电话本程序:键盘输入人名,空格之后电话号码。回车之后,把人名添加到菜单项。当点击菜单时,显示电话本信息。 1, 给View添加private 的 CString m_strLine;//存储输入的字符串 CMenu m_menu;//菜单 int m_nIndex;//用于计数,是不是第一次 2, 添加View的public成员 CStringArray m_strArray; 3, 添加View的WM_CHAR消息响应函数 4, 在View::OnChar中添加如下代码 CClientDC dc(this); if(0x0d==nChar) { if(0==++m_nIndex)//如果是第一次,就创建菜单 { m_menu.CreatePopupMenu(); GetParent()->GetMenu()->AppendMenu(MF_POPUP, (UINT)m_menu.m_hMenu,"PhoneBook"); GetParent()->DrawMenuBar();//立即显示菜单 } m_menu.AppendMenu(MF_STRING,IDM_PHONE1+m_nIndex,m_strLine.Left(m_strLine.Find(' '))); m_strArray.Add(m_strLine); m_strLine.Empty(); Invalidate();//擦除先前的背景,防止重叠显示 } else { m_strLine+=nChar; dc.TextOut(0,0,m_strLine); } 5, 添加菜单的响应函数 方法一: 1) 在Toolbar上添加一个菜单,其弹出项为ID号为IDM_PHONE1, IDM_PHONE2, IDM_PHONE3。 2) 在Rsource.h中添加 #define IDM_PHONE1 201 #define IDM_PHONE2 202 #define IDM_PHONE3 203 3) 利用ClassWizard给菜单项添加响应函数,并编辑函数 4) 删除菜单项(其响应函数会依然存在,并且4步中利用了ID号,可以对应过去) 方法二: 视频没有提供,但我感觉应该有其他方法。方法一太死板 6, 另:由框架类捕获菜单响应,而不是由View处理,可以节约代码 1) 给MainFrame添加WM_COMMAND消息响应函数 2) 在其中添加如下代码 int MenuCmdId=LOWORD(wParam); CMenu2View *pView=(CMenu2View*)GetActiveView(); if(MenuCmdId>=IDM_PHONE1 &&MenuCmdIdm_strArray.GetSize()) { CClientDC dc(pView); dc.TextOut(0,0,pView->m_strArray.GetAt(MenuCmdId-IDM_PHONE1)); return TRUE; } 第6点提供的方法,不能解决第5点提出方法的限制,响应菜单还是和事先编好的ID相对应,不能动态增加 其他注意的地方: 1, 弹出菜单(Pop-up)是不能用来作命令响应的。此项设置在菜单属性栏内。 2, 将Toolbar的ID设为菜单的ID,二者即发生了关联。 3, 在计算子菜单菜单项的索引的时候,分隔栏符也算索引。 4, 消息影射函数 Cpp文件中 BEGIN_MESSAGE_MAP(CMenu2View, CView) //{{AFX_MSG_MAP(CMenu2View) //位置1 //}}AFX_MSG_MAP 位置2 END_MESSAGE_MAP() 头文件中 //{{AFX_MSG(CMenu2View) 位置3 //}}AFX_MSG 位置4 DECLARE_MESSAGE_MAP() 消息映射在位置1和位置2不一样,可以调用Classwizard看到修改结果 位置3和位置4的函数声明,随便放1或2都可以,没有影响 第六章 对话框编程(1) 我们假设您已经: 1,知道如何创建一个单文档的App Wizard 2,知道C++ 类、函数重载等简单知识 3,知道如何给View类或者Doc文档添加成员变量 4,会用MFC的IDE调试工具最好,那么本文的程序您可以copy去调试 5,知道如何为某个框架类添加虚函数或消息处理函数 1,动态创建按钮 1)增加全局变量 CButton m_btn; 2)在需要创建的地方 //ID号123可以随意改变 if(!m_btn.m_hWnd) m_btn.Create("维新",BS_DEFPUSHBUTTON | WS_VISIBLE | WS_CHILD,CRect(0,0,100,100),this,123); else m_btn.DestroyWindow(); 2,复制控件 在dlg上添加控件时,按住ctrl键,拖动就可以复制一模一样的控件 3,控件对齐 在对话框上多个控件对齐,可以用layout菜单或者左下角toolbar 4,动态编辑static静态文本框 CString str; if(GetDlgItem(IDC_NUMBER1)->GetWindowText(str),str=="Number1:") GetDlgItem(IDC_NUMBER1)->SetWindowText("数值1:"); else GetDlgItem(IDC_NUMBER1)->SetWindowText("Number1:"); 要让static静态文本框响应消息,需要复选上notify选项 5,Edit文本框 获取/设置文本内容 1)方法1 char ch[10]; GetDlgItem(IDC_EDIT1)->GetWindowText(ch1,10); GetDlgItem(IDC_EDIT3)->SetWindowText(itoa(atoi(ch1),ch1,10)); 2)方法2 GetDlgItemText(IDC_EDIT1,ch1,10); SetDlgItemText(IDC_EDIT3,itoa(atoi(ch1),ch1,10)); 3)方法3 SetDlgItemInt(IDC_EDIT3,GetDlgItemInt(IDC_EDIT1));//对整型数字的字符串有用 4)关联变量法 对每个Edit控件关联一个变量,设置后记得用UpdateData() 对于显示数字类的文本框,可以定义value和control两种类型变量 DoDataExchange() called by the framework to exchange and validate dialog data The framework automatically calls UpdateData with bSaveAndValidate set to FALSE when a modal dialog box is created in the default implementation of CDialog::OnInitDialog. 5)利用WM_GETTEXT消息处理获取文本 char ch1[10]; 可以用以下四种方法(m_edit1为关联的控制型变量) ::SendMessage(GetDlgItem(IDC_EDIT1)->m_hWnd,WM_GETTEXT,10,(LPARAM)ch1); ::SendMessage(m_edit1.m_hWnd,WM_GETTEXT,10,(LPARAM)ch1); GetDlgItem(IDC_EDIT1)->SendMessage(WM_GETTEXT,10,(LPARAM)ch1); m_edit1.SendMessage(WM_GETTEXT,10,(LPARAM)ch1); 利用WM_SETTEXT消息处理设置文本 m_edit3.SendMessage(WM_SETTEXT,0,(LPARAM)ch3); 6)直接对对话框控件进行消息发送 SendDlgItemMessage(IDC_EDIT1,WM_GETTEXT,10,(LPARAM)ch1);//获取文本 SendDlgItemMessage(IDC_EDIT3,WM_SETTEXT,0,(LPARAM)ch3);//设置文本 SendDlgItemMessage(IDC_EDIT3,EM_SETSEL,0,-1); 7)利用EM_GETSEL,EM_SETSEL的消息处理 6,对话框收缩 点击"收缩<<"对话框收缩,点击"扩展>>"则扩展, 请看例子代码 7,多个edit框用Enter键切换的方法,三种方法 1)捕获键盘消息,在消息函数中处理(未提供) 2)修改Edit的窗口过程:自己写窗口过程替代原来的窗口过程(比较麻烦的方法) (1)定义窗口过程类型变量 WNDPROC prevProc; (2)定义窗口过程函数 LRESULT CALLBACK WinSunProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam ) { if(uMsg==WM_CHAR && wParam==0x0d)//如果是回车 { //::SetFocus(::GetNextWindow(hwnd,GW_HWNDNEXT));//获取下一窗口句柄方法一 //SetFocus(::GetWindow(hwnd,GW_HWNDNEXT));//方法二 SetFocus(::GetNextDlgTabItem(::GetParent(hwnd),hwnd,FALSE));//方法三 return 1; } else return prevProc(hwnd,uMsg,wParam,lParam); } (3)添加WM_INITDIALOG对应的函数 (4)在OnInitDialog中添加 prevProc=(WNDPROC)SetWindowLong(GetDlgItem(IDC_EDIT1)->m_hWnd,GWL_WNDPROC,(LONG)WinSunProc); (5)注意 edit控件 MultiLine复选属性选/不选的不同 SetWindowLong changes an attribute of the specified window. 3)在OnOK(default button对应的函数) GetFocus()->GetNextWindow()->SetFocus();//注意最后一个窗口时要判断,不然获取出错 GetFocus()->GetWindow(GW_HWNDNEXT)->SetFocus();//注意同上 GetNextDlgTabItem(GetFocus())->SetFocus(); 注意: 对话框初始的OK的ID号为IDOK,即使删除按钮(OnOk函数存在),依然会响应OnOk函数 第七章 对话框编程(2) 我们假设您已经: 1,知道如何创建一个单文档的App Wizard 2,知道C++ 类、函数重载等简单知识 3,知道如何给View类或者Doc文档添加成员变量 4,会用MFC的IDE调试工具最好,那么本文的程序您可以copy去调试 5,知道如何为某个框架类添加虚函数或消息处理函数 一、 简单的逃跑按钮 //注意下列方法,定义一个类为了捕获鼠标移动点的方便 1,创建一个基于对话框的MFC AppWizard工程 2,在View窗口点右键,添加一个自定义的类(或者用Classwizard工具添加),基类为CButton class CWeiXinBtn : public CButton 3,给CWeiXinBtn类添加成员变量CWeiXinBtn* m_pBtn; 4,给对话框添加俩按钮,每个按钮都关联一个CWeiXinBtn的变量 5,在OnInitDialog添加 m_btn1.m_pBtn=&m_btn2; m_btn2.m_pBtn=&m_btn1; 6,CWeiXinBtn::OnMouseMove中添加交换显示的语句 ShowWindow(SW_HIDE); m_pBtn->ShowWindow(SW_SHOW); 大功告成! 当然可以SetWindowPos函数实现更客观 二、属性页的编辑 1,CPropertyPage类 在sheet的构造函数中添加propertyPage prop1sheet::AddPage 把页面添加到sheet 2,属性页变向导类型 prop1sheep.SetWindowMode();//向导模式语句 prop1sheet.DoModal(); 3,第一页的"上一步",最后一页的"下一步"取消方法 在第一个/最后一页属性页类添加虚函数PnSetActive,并在其中添加 ((CPropertySheet *)GetParent())->SetWizardButtons(PSWIZB_NEXT);//第一页 ((CPropertySheet *)GetParent())->SetWizardButtons(PSWIZB_BACK|PSWIZB_NEXT);//中间的页 ((CPropertySheet *)GetParent())->SetWizardButtons(PSWIZB_BACK|PSWIZB_FINISH);//最后一页 4,"下一步"之前,检查是否已完成“选择”等 在PropertyPage的OnWizardNext函数中检查 5,编辑对话框/属性页上的ComBox控件 ((CComoBox *)GetDlgItmem(IDC_ComBOX1))->AddString(" ");//增加选项 6,获取List(ComBox)控件,并进行编辑 int sel=((CComoBox *)GetDlgItmem(IDC_ComBOX1))->GetCurSel(); CString m_str; ((CComoBox *)GetDlgItmem(IDC_ComBOX1))->GetLBText(sel,&m_str);//取出用户的选择 7,窗口IDOK==xxx.DoModal()后,其上面的控件生命期仍有效,所以可以用变量接受其值 8,List控件的sort属性选中/不选中,表示是否自动排序,注意有时不需要自己排序 下面讲述一个属性页对话框的使用例程。 1,VC++经常问题。 不能为已建好的类打开文件 在VC中为资源(对话框、属性页等)添加类时,打开classwizard=>添加类,输入类名,选择baseclass,点OK之后,弹出不能打开文件的错误"Unable to open the file(XXX.h,XXX.cpp) for class xxx" 解决办法:删除类信息文件XXX.clw;再次调用classwizard,重新产生一个xxx.clw即可 2,属性页资源的增加 在Resource View里Dialog处点击右键InsertèDialogèIDD_PROPPAGE_LARGE(English(U.S.)) 注意看属性页资源的属性:类型-Child,Border-Thin,System menu不复选,More style中复选了Disabled 也可以通过修改普通对话框,而成为属性页。 3,创建类 给属性页对话框添加类的时候,基类选为CPropertyPage,而不是CDialog 4,创建属性表单 利用Classwizard插入一个新的类,基类选为CPropertySheet 5,给属性表单添加三个public变量 CProp1 m_prop1; CProp2 m_prop2; CProp3 m_prop3; 6,在属性表单的两个构造函数增加表单 AddPage(&m_prop1); AddPage(&m_prop2); AddPage(&m_prop3); 7,在View类添加一个菜单项,添加响应函数,添加下列语句 CPropSheet propSheet("维新属性表单程序"); //propSheet.SetWizardMode();//向导类时增加这一句 if(ID_WIZFINISH==propSheet.DoModal()) { //获取各个表单项的选项,仅作为例子 m_iOccupation=propSheet.m_prop1.m_occupation; m_strWorkAddr=propSheet.m_prop1.m_workAddr; m_bLike[0]=propSheet.m_prop2.m_football; m_strSalary=propSheet.m_prop3.m_strSalary; Invalidate(); } 属性表单创建完毕。属性页具体内容的编辑和内容的显示过程省略 向导类的创建 1,在Domodal之前添加 propSheet.SetWizardMode(); 2,设置最初/末页的“上一步”和“下一步” 在CProp1类处右键,加载需函数OnSetActive,并在CProp1::OnSetActive中添加 ((CPropertySheet*)GetParent())->SetWizardButtons(PSWIZB_NEXT); 在CProp4::OnSetActive中添加 ((CPropertySheet*)GetParent())->SetWizardButtons(PSWIZB_BACK | PSWIZB_FINISH); 为每一页添加限制条件:只有在当页进行必要操作后,才能“下一步” 1, 为每个属性页添加虚函数DoDataExchange,其中不添加代码 2, 为最后一页添加虚函数OnWizardFinish,其他页添加OnWizardNext函数,并在其中添加“下一步”的判断条件 数据交换 UpdateData(TRUE);//从控件中取回值 UpdateData(FALSE);//给变量值赋赋控件 第八章 MFC中指针的获取 我们假设您已经: 1,知道如何创建一个单文档的App Wizard 2,知道C++ 类、函数重载等简单知识 3,知道如何给View类或者Doc文档添加成员变量 4,会用MFC的IDE调试工具最好,那么本文的程序您可以copy去调试 5,知道如何为某个框架类添加虚函数或消息处理函数 因为在开始学MFC时总是不知道如何获取各类指针,很是浪费了不少时间。 可能也有正在学习VC的人受此苦扰,那希望此文能有帮助 MFC应用程序中指针的使用 1) 在View中获得Doc指针 2) 在App中获得MainFrame指针 3) 在View中获得MainFrame指针 4) 获得View(已建立)指针 5) 获得当前文档指针 6) 获得状态栏与工具栏指针 7) 获得状态栏与工具栏变量 8) 在Mainframe获得菜单指针 9) 在任何类中获得应用程序类 10) 从文档类取得视图类的指针(1) 11) 在App中获得文档模板指针 12) 从文档模板获得文档类指针 13) 在文档类中获得文档模板指针 14) 从文档类取得视图类的指针(2) 15) 从一个视图类取得另一视图类的指针 VC中编程对于刚刚开始学习的同学,最大的障碍和问题就是消息机制和指针获取与 操作。其实这些内容基本上是每本VC学习工具书上必讲的内容,而且通过MSDN很多 问题都能解决。下面文字主要是个人在编程中指针使用的一些体会,一般我们使用的框架是VC提供的Wizard生成的MFC App Wizard(exe)框架, 无论是多文档还是单文档,都存在指针获取和操作问题。下面这节内容主要是一般 的框架,然后再讲多线程中的指针使用。使用到的类需要包含响应的头文件。首先 一般获得本类(视,文档,对话框都支持)实例指针this,用this的目的,主要可以通 过类中的函数向其他类或者函数中发指针,以便于在非本类中操作和使用本类中的 功能。 1) 在View中获得Doc指针 CYouSDIDoc *pDoc=GetDocument();一个视只能有一个文档。 2) 在App中获得MainFrame指针 CWinApp 中的 m_pMainWnd变量就是MainFrame的指针也可以: CMainFrame *pMain =(CMainFrame *)AfxGetMainWnd(); 3) 在View中获得MainFrame指针 CMainFrame *pMain=(CmaimFrame *)AfxGetApp()->m_pMainWnd; 4) 获得View(已建立)指针 CMainFrame *pMain=(CmaimFrame *)AfxGetApp()->m_pMainWnd; CyouView *pView=(CyouView *)pMain->GetActiveView(); 5) 获得当前文档指针 CDocument * pCurrentDoc =(CFrameWnd *)m_pMainWnd->GetActiveDocument(); 6) 获得状态栏与工具栏指针 CStatusBar * pStatusBar=(CStatusBar *)AfxGetMainWnd()->GetDescendantWindow(AFX_IDW_STATUS_BAR); CToolBar * pToolBar=(CtoolBar *)AfxGetMainWnd()->GetDescendantWindow(AFX_IDW_TOOLBAR); 7) 如果框架中加入工具栏和状态栏变量还可以这样 (CMainFrame *)GetParent()->m_wndToolBar; (CMainFrame *)GetParent()->m_wndStatusBar; 8) 在Mainframe获得菜单指针 CMenu *pMenu=m_pMainWnd->GetMenu(); 9) 在任何类中获得应用程序类用MFC全局函数AfxGetApp()获得。 10) 从文档类取得视图类的指针 我是从http://download.cqcnc.com/soft/program/article/vc/vc405.html学到的,从文档获得视图类指针目的一般为了控制同一文档的多个视图的定位问题,我的体会特别是文字处理CEditView当产生多个视图类时,这个功能是非常需要的。 CDocument类提供了两个函数用于视图类的定位: GetFirstViewPosition()和GetNextView() virtual POSITION GetFirstViewPosition() const; virtual CView* GetNextView(POSITION& rPosition) const; 注意:GetNextView()括号中的参数用的是引用方式,因此执行后值可能改变。 GetFirstViewPosition()用于返回第一个视图位置(返回的并非视图类指针,而是一个POSITION类型值),GetNextView()有两个功能:返回下一个视图类的指针以及用引用调用的方式来改变传入的POSITION类型参数的值。很明显,在Test程序中,只有 一个视图类,因此只需将这两个函数调用一次即可得到CTestView的指针如下(需定义一个POSITION结构变量来辅助操作): CTestView* pTestView; POSITION pos=GetFirstViewPosition(); pTestView=GetNextView(pos); 这样,便可到了CTestView类的指针pTestView.执行完几句后,变量pos=NULL,因为没有下一个视图类,自然也没有下一个视图类的POSITION.但是这几条语句太简单,不具有太强的通用性和安全特征;当象前面说的那样,当要在多个视图为中返回某个指定类的指针时,我们需要遍历所有视图类,直到找到指定类为止。判断一个类指针指向的是否某个类的实例时,可用IsKindOf()成员函数时行检查,如: pView->IsKindOf(RUNTIME_CLASS(CTestView)); 即可检查pView所指是否是CTestView类。 有了以上基础,我们已经可以从文档类取得任何类的指针。为了方便,我们将其作为一个文档类的成员函数,它有一个参数,表示要获得哪个类的指针。实现如下: CView* CTestDoc::GetView(CRuntimeClass* pClass) { CView* pView; POSITION pos=GetFirstViewPosition(); while(pos!=NULL){ pView=GetNextView(pos); if(!pView->IsKindOf(pClass)) break; } if(!pView->IsKindOf(pClass)){ AfxMessageBox("Connt Locate the View.\r\n http://www.VCKBASE.com"); return NULL; } return pView; } 其中用了两次视图类的成员函数IsKindOf()来判断,是因为退出while循环有三种可能: 1.pos为NULL,即已经不存在下一个视图类供操作; 2.pView已符合要求。 1和2同是满足。这是因为GetNextView()的功能是将当前视图指针改变成一个视图的位置同时返回当前视图指针,因此pos是pView的下一个视图类的POSITION,完全有可能既是pos==NULL又是pView符合需要。当所需的视图是最后一个视图是最后一个视图类时就如引。因此需采用两次判断。 使用该函数应遵循如下格式(以取得CTestView指针为例): CTestView* pTestView=(CTestView*)GetView(RUNTIME_CLASS(CTestView)); RUNTIME_CLASS是一个宏,可以简单地理解它的作用:将类的名字转化为CRuntimeClass为指针。至于强制类型转换也是为了安全特性考虑的,因为从同一个基类之间的指针类型是互相兼容的。这种强制类型转换也许并不必要,但能避免一些可能出现的麻烦。 3.从一个视图类取得另一视图类的指针 综合1和2,很容易得出视图类之间互相获得 指针的方法:就是用文档类作中转,先用1的方法得到文档类的指针,再用2的方法,以文档类的视图定位函数取得另一个视图类。同样,可以实现成一个函数: (假设要从CTestAView中取得指向其它视图类的指针) CView* CTestAView::GetView(CRuntimeClass* pClass) { CTestDoc* pDoc=(CTestDoc*)GetDocument(); CView* pView; POSITION pos=pDoc->GetFirstViewPosition(); while(pos!=NULL){ pView=pDoc->GetNextView(pos); if(!pView->IsKindOf(pClass)) break; } if(!pView->IsKindOf(pClass)){ AfxMessageBox("Connt Locate the View."); return NULL; } return pView; } 这个函数和2中的GetView()相比,一是多了第一句以取得文档类指针,二是在GetFirstViewPosition()和GetNextView()前加上了文档类指针,以表示它们是文档类成员函数。有了此函数;当要从CTestAView中取得CTestBView的指针时,只需如下:CTestBView* pTestbView=(CTestView*)GetView(RUNTIME_CLASS(CTestBView));   11)对于单文档中也可以加入多个文档模板,但是一般的就使用MDI方式开发多文档模板,其方法与上述视图的获取方法很接近,这里稍做解释,如果不清楚,请查阅MSDN,(以下四个内容(11、12、13、14)来源: http://sanjianxia.myrice.com/vc/vc45.htm) 可以用CWinApp::GetFirstDocTemplatePostion获得应用程序注册的第一个文档模板的位置;利用该值来调用CWinApp::GetNextDocTemplate函数,获得第一个CDocTemplate对象指针。 POSITION GetFirstDocTemplate( ) const; CDocTemplate *GetNextDocTemplate( POSITION & pos ) const; 第二个函数返回由pos 标识的文档模板。POSITION是MFC定义的一个用于迭代或对象指针检索的值。通过这两个函数,应用程序可以遍历整个文档模板列表。如果被检索的文档模板是模板列表中的最后一个,则pos参数被置为NULL。 12)一个文档模板可以有多个文档,每个文档模板都保留并维护了一个所有对应文档的指针列表。 用CDocTemplate::GetFirstDocPosition函数获得与文档模板相关的文档集合中第一个文档的位置,并用POSITION值作为CDocTemplate::GetNextDoc的参数来重复遍历与 模板相关的文档列表。函数原形为: viaual POSITION GetFirstDocPosition( ) const = 0; visual CDocument *GetNextDoc(POSITION & rPos) const = 0; 如果列表为空,则rPos被置为NULL. 13)在文档中可以调用CDocument::GetDocTemplate获得指向该文档模板的指针。 函数原形如下: CDocTemplate * GetDocTemplate ( ) const; 如果该文档不属于文档模板管理,则返回值为NULL。 14)一个文档可以有多个视。每一个文档都保留并维护一个所有相关视的列表。 CDocument::AddView将一个视连接到文档上,将该视加入到文档相联系的视的列表中,并将视的文档指针指向该文档。当有File/New、File/Open、Windows/New或Window/Split的命令而将一个新创建的视的对象连接到文档上时, MFC会自动调用该函数,框架通过文档/视的结构将文档和视联系起来。当然,程序员也可以根据自 己的需要调用该函数。 Virtual POSITION GetFirstViewPosition( ) const; Virtual CView * GetNextView( POSITION &rPosition) cosnt; 应用程序可以调用CDocument::GetFirstViewPosition返回与调用文档相联系的视的列表中的第一个视的位置,并调用CDocument::GetNextView返回指定位置的视,并将rPositon的值置为列表中下一个视的POSITION值。如果找到的视为列表中的最后一个视,则将rPosition置为NULL. 15)从一个视图类取得另一视图类的指针 这个应用在多视的应用程序中很多见,一般如果自己在主程序或者主框架中做好变量记号,也可以获得,还有比较通用的就是用文档类作中转,以文档类的视图遍历定位,取得另一个视图类。这个功能从本文第10项中可以得到。   _______________________________________________________________ 访问应用程序的其它类 获得CWinApp: -在CMainFrame,CChildFrame,CDocument,CView中直接调用AfxGetApp()或用theApp -在其它类中只能用AfxGetApp() 获得CMainFrame: -在CMinApp中用AfxGetMainWnd()或者m_pMainWnd -在CChildFrame中可用GetParentFrame() -在其它类中用AfxGetMainWnd() 获得CChildFrame: -在CView中用GetParentFrame() -在CMainFrame中用MDIGetActive()或GetActiveFrame() -在其它类中用AfxGetMainWnd()->MDIGetActive()或AfxGetMainWnd()->GetActiveFrame() 获得CDocument: -在CView中用GetDocument() -在CChildFrame中用GetActiveView()->GetDocument() -在CMainFrame中用 -if SDI:GetActiveView()->GetDocument() -if MDI:MDIGetActive()->GetActiveView()->GetDocument() -在其它类中 -if SDI:AfxGetMainWnd()->GetActiveView()->GetDocument() -if MDI:AfxGetMainWnd()->MDIGetActive()->GetActiveView()->GetDocument() 获得CView: -在CDocument中 POSITION pos = GetFirstViewPosition();GetNextView(pos) -在CChildFrame中 GetActiveView() -在CMainFrame中 -if SDI:GetActiveView() -if MDI:MDIGetActive()->GetActiveView() -在其它类中 -if SDI:AfxGetMainWnd()->GetActiveView() -if MDI:AfxGetMainWnd()->MDIGetActive()->GetActiveView() 访问应用程序的其它类 获得CWinApp: -在CMainFrame,CChildFrame,CDocument,CView中直接调用AfxGetApp()或用theApp -在其它类中只能用AfxGetApp() 获得CMainFrame: -在CMinApp中用AfxGetMainWnd()或者m_pMainWnd -在CChildFrame中可用GetParentFrame() -在其它类中用AfxGetMainWnd() 获得CChildFrame: -在CView中用GetParentFrame() -在CMainFrame中用MDIGetActive()或GetActiveFrame() -在其它类中用AfxGetMainWnd()->MDIGetActive()或AfxGetMainWnd()->GetActiveFrame() 获得CDocument: -在CView中用GetDocument() -在CChildFrame中用GetActiveView()->GetDocument() -在CMainFrame中用 -if SDI:GetActiveView()->GetDocument() -if MDI:MDIGetActive()->GetActiveView()->GetDocument() -在其它类中 -if SDI:AfxGetMainWnd()->GetActiveView()->GetDocument() -if MDI:AfxGetMainWnd()->MDIGetActive()->GetActiveView()->GetDocument() 获得CView: -在CDocument中 POSITION pos = GetFirstViewPosition();GetNextView(pos) -在CChildFrame中 GetActiveView() -在CMainFrame中 -if SDI:GetActiveView() -if MDI:MDIGetActive()->GetActiveView() -在其它类中 -if SDI:AfxGetMainWnd()->GetActiveView() -if MDI:AfxGetMainWnd()->MDIGetActive()->GetActiveView() 不过要注意在doc中要取得view的指针C*View要注意类C*View声明的问题, 因为默认情况下,mfc在*View.h中已经包含了*Doc.h,如果在*Doc.h中包含 *View.h,就会引起嵌套包含问题,这样要在*Doc.h中加入 class C*View; 而在*Doc.cpp中加入 #include "*View.h" ////////////////////////////////////////////////////////////////// 其实完全可以在CYourApp中添加各种视或文档的指针,在那些视或文档初始化的时候将指针传给CYourApp中的对应变量,这样以后不管在哪用上面指针只需(CYourApp*)AfxGetApp()取其属性变量即可,明了而且清楚更是方便我一直专门操作的说:) ////////////////////////////////////////////////////////////////// 我先抛块砖,有玉的砸过来! 在何时何地,你都可以通过以下方法精确的得到任何一个对象(Application,DocTemplate,Document,View,Frame) 1。通过AfxGetApp()得到当前的App对象; 2。通过AfxGetMainWnd()得到主窗口; 3。通过CMDIFrameWnd::GetActiveFrame得到当前活动窗口; 4。通过GetNextWindow()遍例所有的子窗口;(如果要得到你想要的子窗口,可以通过特定的成员变量来标); 5。通过CWinApp::GetFirstDocTemplatePostion()以及CWinApp::GetNextDocTemplate()的组合应用来遍历所有的DocTemplate对象,并且用CDocTemplate::GetDocString()来判断当前得到的文档莫板对象是哪个。 6。通过CDocTemplate::GetFirstDocPosition()以及CDocTemplate的GetNextDoc()组合来遍历所有的该模板的文档对象,并用CDocument::GetDocTemplate()来得到文档模板,用CDocment::GetTitle() 或者GetPathName()来判断当前的文档是哪个。 7。通过CDocuemt的GetFirstViewPositon()以及GetNextView()来遍历视图对象,一般通过访问View的成员变量来区别各个视图;通过CView::GetDocument()来得到文档对象; 8。Frame->View: 通过GetActiveView方法; 9。Frame->Doc:通过GetActiveDocument(); 10。View->Frame:GetParentFrame(); 11。View->Doc:GetDocuemt()//前面已经说了。 12。Doc->View:前面说了; 13。Doc->Frame:不知道有没有很直接的方法。 第九章 应用程序外观修改 我们假设您已经: 1,知道如何创建一个单文档的App Wizard 2,知道C++ 类、函数重载等简单知识 3,知道如何给View类或者Doc文档添加成员变量 4,会用MFC的IDE调试工具最好,那么本文的程序您可以copy去调试 5,知道如何为某个框架类添加虚函数或消息处理函数   更改标题栏图标 事先已经添加IDI_ICON1、IDI_ICON2、IDI_ICON3资源。注意要实现代码,须把变量定义成全局或者用其他方式 HICON m_hI1=LoadIcon(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDI_ICON1)); HICON m_hI2=LoadIcon(theApp.m_hInstance,MAKEINTRESOURCE(IDI_ICON2)); HICON m_hI3=LoadIcon(AfxGetApp()->m_hInstance,MAKEINTRESOURCE(IDI_ICON3)); SetClassLong(m_hWnd,GCL_HICON,(LONG)m_h1); 一、在状态栏上添加时间显示 1, 在String_Table中添加IDS_NEW 2, IDS_NEW添加到CMainFrame的indicator中 3, CMainFrame::OnCreate()中return 之前 CTime t=CTime::GetCurrentTIme(); CString str=t.Format("%H:%M:%S"); m_wndStatusBar.SetPaneText(1,str); //int index=m_wndStatusBar.CommandToIndex(IDS_NEW); //m_wndStatusBar.SetPaneText(index,str); //调整显示宽度 CClinetDC dc(this); CSize sz=dc.GetTextExtent(str); m_wndStatusBar.SetPaneInfo(index,IDS_NEW,SBPS_NORMAL,sz.cx); 4,为适时更新设置定时器。在CMainFrame::OnTimer()中复制下面代码如下: CTime t=CTime::GetCurrentTIme(); CString str=t.Format("%H:%M:%S"); //调整显示宽度 CClinetDC dc(this); CSize sz=dc.GetTextExtent(str); m_wndStatusBar.SetPaneInfo(1,IDS_NEW,SBPS_NORMAL,sz.cx); m_wndStatusBar.SetPaneText(1,str); 5,在CMainFrame::()启动计时器 SetTimer 二、进度栏 一)创建一个进度栏 1,在MainFrame.h中添加CProgressCtrl对象 CProgressCtrl m_progress; 2,在CMainFrame::OnCreate()中返回前创建之 m_progress.Create(WS_CHILD | WS_VISIBLE,CRect(100,100,200,120),this,123); 3,设置位置 m_progress.SetPos(50); 4,垂直进度栏 m_progress.Create(WS_CHILD | PBS_VERTICAL,CRect(100,100,120,200),this,123); 5,进度栏放入状态栏窗格 CRect r; m_wndStatusBar.GetItemRect(2,&r); //创建时大小和父窗口都改变 m_progress.Create(WS_CHILD | WS_VISIBLE |PBS_SMOOTH,r,&m_wndStatusBar,123); 以上即为实现代码,但是放在OnCreate函数中不能实现:因为OnCreate()返回前状态栏的位置还没有确定,所以无法获得各个子窗格位置,只能用自定义消息办法响应。 1)定义消息 在MainFrame.h前面添加 #define UM_PROGREE WM_USER 2)消息响应函数原形的声明宏 在MainFrame.h中添加 afx_msg void OnProgress();//可以添加两个参数 3)消息映射 在MainFrame.cpp中//}}AFX_MSG_MAP之后添加映射 ON_MESSAGE(UM_PROGRESS,OnProgress) 4)实现消息响应函数 void CMAinFrame::OnProgress() { CRect r; m_wndStatusBar.GetItemRect(2,&r); m_progress.Create(WS_CHILD | WS_VISIBLE,r,&m_wndStatusBar,123);//创建时大小和父窗口都改变 m_progress.SetPos(50); } 5)在OnCreate()中发送消息 PostMessage(UM_PROGRESS);//注意不要用SendMessage(); 此时的进度栏在状态栏上的位置时固定的,不会随状态栏位置变化而变化 6)使进度栏与状态栏位置相关联 把OnCreate()中的PostMessage()删除使之失效. 首先添加OnPaint()消息函数 窗口改变重绘会发送WM_PAINT消息,所以只要在OnPaint函数中处理即可.以下代码添加到OnPaint()中 CRect r; m_wndStatusBar.GetItemRect(2,&r); if(!m_progress.m_hWnd)m_progress.Create(WS_CHILD | WS_VISIBLE,r,&m_wndStatusBar,123);//创建时大小和父窗口都改变 else m_progress.MoveWindow(r);//或者SetWindowPos()函数 m_progress.SetPos(50); 二)将移动坐标适时显示到状态栏上 1,添加OmMouseMove()函数 2,将状态栏变量访问权限改变,备用 在MainFrame.h中 m_wndStatusBar变量改为公用 3,在view.cpp中包含MainFrame.h 4,在OnMouseMove()中添加 CString s; s.Format("x=%d,y=%d",point.x,point.y); //将以下情况之一添加添加此处即可实现 1)((CMainFrame*)GetParent())->m_wndStatusBar.SetWindowText(str); 2)((CMainFrame*)GetParent())->SetMessageText(str); 3)((CMainFrame*)GetParent())->GetMessageBar()->SetMessageText(str);//状态栏可以不公有 4)GetDescenddantWindow()根据ID号搜索子孙窗口,直到找到指针 主框架状态栏ID号为AFX_IDW_STATUS_BAR GetParent()->GetDescenddantWindow(AFX_IDW_STATUS_BAR)->SetWindowText(str); 三、给软件增加启动画面 1,菜单Project=>Add to Project=>Component and Control,对话框中选择Visual C++ Components,选择Splash screen选择插入,选OK并关闭 2,系统自动增加CSplashWnd类; 在CMainFrame::OnCreate()中添加了CSplashWnd::SHowSplashScreen(this); 3,在CSplashWnd::OnCreate()中有SetTimer(1,750,NULL);设置750ms的启动画面显示间隔 4,自定义显示界面的话,可以依照这个类进行 第十章 图形绘制与通用对话框 我们假设您已经: 1,知道如何创建一个单文档的App Wizard 2,知道C++ 类、函数重载等简单知识 3,知道如何给View类或者Doc文档添加成员变量 4,会用MFC的IDE调试工具最好,那么本文的程序您可以copy去调试 5,知道如何为某个框架类添加虚函数或消息处理函数 添加对话框,再用ClassWizard添加new class之后,出现找不到类的提示,可以删除.clw文件,重编译即可 一、绘制图形 CClientDC dc(this); CPen pen(PS_SOLID,1,RGB(255,0,0)); dc.SelceObject(&pen); //此处可以调用函数进行绘制了 dc.SetPixel(point,RGB(255,0,0)); dc.MoveTo();dc.LineTo();dc.Rectangle() 二、透明画刷 在绘图前添加 CBrush *pBrush=CBrush::FromHandle(GetStockObject(NULL_BRUSH)); dc.SelctObject(pBrush); 三、颜色对话框 数据m_cc CColorDialog dlg; dlg.m_cc.Flags|=CC_RGBINIT;//可以再|CC_FULLOPEN,但不能写成dlg.m_cc.Flags=CC_RGBINIT; dlg.m_cc.rgbResult=m_clr; if(IDOK==dlg.Domodal()) m_clr=dlg.m_cc.rgbResult; 四、字体对话框 数据成员m_cf CFont m_font; CString m_strFontName; CFontDialog dlg; if(IDOK==dlg.Domodal()) { m_font.CreateFontIndirect(dlg.m_cf.lpLogFont); -----1 m_strFontName=dlg.m_cf.lpLogFont->lfFaceName; Invalidate(); } 在OnDraw()中添加 CFont *pOldFont=pDC->SelectObject(&m_font); pDC->TextOut(0,0,m_strFontName); pDC->SelectObject(pOldFont); 运行,第二次选择字体时出错(二次初始化),将上面程序语句1修改为 if(m_font.m_hObject)m_font.DeleteObject(); m_font.CreateFontIndirect(dlg.m_cf.lpLogFont); 控件和成员变量关联时 控件值=>成员变量 UpdateData(); 成员变量可以直接赋值给控件并显示 五、对话框背景色 1,消息WM_CTLCOLOR 添加WM_CTLCOLOR消息处理函数 CBrush m_brush; m_brush.CreateSolidBrush(RGB(0,255,0)); 在CXXX::OnCtlColor()中返回m_brush即可; 2,对话框上如何精确获得控件? 在CXX::OnCtlColor()中添加 1)对按静态框、组合框等可以如下 if(pWnd->GetDlgCtrID()==IDC_STATIC1) { pDC->SetTextColor(RGB(255,0,0)); pDC->SetBkMode(TRANSPARENT);//设置文字的背景色 return m_brush; } 2)对文本框(单行编辑控件)如下 if(pWnd->GetDlgCtrID()==IDC_EDIT1) { pDC->SetTextColor(RGB(255,0,0)); pDC->SetBkColor(RGB(255,0,0));//设置文本框的背景色 return m_brush; } 3,改变静态框显示文字类型 CFont m_font; m_font.CreatePointFont(200,"华文行楷"); if(pWnd->GetDlgCtrID()==IDC_STATIC1) pDC->SelectObject(&m_font); 4,OK按钮 1)在对话框类 =>Insert=>New Class添加以CButton为基类的类CTstBtn 2)增加CtstBtn的虚函数DrawItem 3)在CtstBtn::DrawItem()中添加 UINT uStyle =DFCS_BUTTONPUSH; ASSERT(lpDrawItemStruct->CtlType == ODT_BUTTON); if(lpDrawItemStruct->itemState & ODS_SELECTED)uStyle |= DFCS_PUSHED; ::DrawFrameControl(lpDrawItemStruct->hDC,&lpDrawItemStruct->rcItem,DFC_BUTTON,uStyle); CString strText; GetWindowText(strText); COLORREF crOldColor = ::SetTextColor(lpDrawItemStruct->hDC,RGB(255,0,0)); ::DrawText(lpDrawItemStruct->hDC,DT_SINGLELINE|DT_VCENTER|DT_CENTER); ::SetTextColor(lpDrawItemStruct->hDC,crOldColor); 4)将OK按钮关联CTstBtn的一个变量,并将按钮的owner_draw的选项选上。这一步就是把对话框上的控件和类关联 六、在窗口中显示位图 1,创建位图 CBitmap bm; bm.LoadBitmap(IDB_BITMAP1); 2,创建兼容DC CDC dcCompatible; dcCompatible.CreateCompatibleDC(pDC); 3,将位图选到兼容DC中 dcCompatible.SelectObject(&bm); 4,将兼容DC中的位图贴到当前DC中。 pDC->BitBlt(rect.left,rect.top,rect.Width(),rect.Height(),&dcCompatible,0,0,SRCCOPY); 七、窗口绘制 1,擦除背景 添加WM_ERASEBKGND消息对应的函数 在CXXXView::OnEraseBkgnd()中添加 CBitmap bm; bm.LoadBitmap(IDB_BITMAP1) 获取位图信息 //BITMAP bmp; //bmp=bm.GetBitmap(&bmp); CDC dcCompatible; dcCompatible.CreateCompatibleDC(pDC); dcCompatible.SelectObject(&bm); CRect rect; GetClientRect(&rect); //BitBlt函数进行1:1的拷贝函数,不能压缩或者拉伸位图 pDC->BitBlt(rect.left,rect.top,rect.Width(),rect.Height(),&dcCompatible,0,0,SRCCOPY); //pDC->StretchBlt(rect.left,rect.top,rect.Width(),rect.Height(),&dcCompatible,0,0,bmp.bmWidth,bmp.bmHeight,SRCCOPY); return TRUE; 并将return CView::OnEraseNkgnd(pDC);注释掉 在这里实现闪烁比较小 2,重新显示 将以上代码复制到OnDraw()函数中,也能正常显示,但是闪烁比较大 题目:创建CDialogBar并在其上放置控件,重复以上的功能,并在列表框中显示位图 第十一章 图形保存和重绘 我们假设您已经: 1,知道如何创建一个单文档的App Wizard 2,知道C++ 类、函数重载等简单知识 3,知道如何给View类或者Doc文档添加成员变量 4,会用MFC的IDE调试工具最好,那么本文的程序您可以copy去调试 5,知道如何为某个框架类添加虚函数或消息处理函数   1,透明画刷 CClientDC dc(this); CBrush *pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH)); dc.SelectObject(pBrush); 2,保存绘制的图像(窗口重绘时仍然存在) CGraphView类中添加m_ptOrigin,m_ptEnd,m_nDrawType三个变量,分别用于保存图像起始点、终止点和图像类型 1)构造一个CGraph类,用于存放每个对象 头文件Graph.h #if !defined(AFX_GRAPH_H__) #define AFX_GRAPH_H__ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 class CGraph { public: UINT m_nDrawType; CPoint m_ptOrigin; CPoint m_ptEnd; CGraph(); CGraph( UINT m_nDrawType,CPoint m_ptOrigin,CPoint m_ptEnd); virtual ~CGraph(); }; 2),因为不知道将会绘制多少图像对象,用集合类来替代复杂的链表结构。View类中定义,添加 CPtrArray m_ptArray; 3),在OnLButtonDown和OnLButtonUp中分别保存两个点,并在OnLButtonUp中添加以下语句,用集合类来保存图像元素 //CGraph graph(m_nDrawType,m_ptOrigin,m_ptEnd);这种方法是错误的,因为局部变量将被销毁 CGraph *pGraph; pGraph = new CGraph(m_nDrawType,m_ptOrigin,m_ptEnd);//在堆中分配内存,不会被释放,生命周期和进程一致,直至delete出现 m_ptArray.Add(&graph); CPaintDC只能在WM_PAINT的消息相应中使用,其创建和析构调用BenginDC和EndDC CClientDC创建时调用GetDC,析构时调用ReleaseDC OnPrepareDC,对CScrollView调用OnPrepareDC会做调整, OnPaint()函数为WM_PAINT的相应函数,如果没有重载OnPaint函数,那么OnPaint基函数是直接调用OnDraw函数。如果重载了OnPaint()函数,那么只有在子类中OnPaint()的调用OnDraw() Win32应用程序接口(API)使用四种坐标空间:世界坐标系空间、页面空间、设备空间、和物理设备空间。应用程序运用世界坐标系空间对图形输出进行旋转、斜切(扭曲)或者反射 Win32 API把世界坐标系空间和页面空间成为逻辑空间;作后一种坐标空间(物理设备空间)通常指应用程序窗口的客户区;但是它也包括整个桌面、完整的窗口(包括框架、标题栏和菜单栏)或打印机的一页或绘图仪的一页纸。物理设备的尺寸随显示器、打印机或绘图仪所设置的尺寸而变化。 如果要在物理设备上绘制输出,windows把一个矩形区域从一个坐标空间拷贝到(映射到)另一个坐标空间,直至最终完整地输出呈现在物理设备上(通常是屏幕或者打印机) 如果该应用程序调用了SetWorldTransform函数,那么映射就从应用程序的世界坐标系空间开始;否则,映射在页面控件中进行。在Windows把矩形区域的每一点从一个空间拷贝到另一个空间时,它采用了一种被称为转换的算法,转换是把对象从一个坐标空间拷贝到另一个坐标空间时改变(转变)这一对象的刀削、方位、和形态,尽管转换把对象看成一个整体,但它也作用于对象中的每一点或每条线。 一个典型转换的例子 图形旋转、斜切的例子在MSDN中using Coordinate spaces and transformations中有TransformAndDraw函数的实现,可以仿照之。例如AutoCAD中对图像的切割、旋转、局部放大等。 OnInitialUpdate,窗口创建完成后,第一个被调用的函数 SetMapMode函数 SetScrollSizes在窗口创建之后调用 滚动窗口中重绘图像时出现的图形移动问题 1,原因:重绘时调用了OnPrepareDC设置了视点坐标 2,保存数据时先调用OnPrepareDC调整,再调用DPtoLP转换后,再保存,就可以了。 另外两种保存图形数据的方法 CMetaFileDC 1,在View.h中添加定义 CMetaFileDC m_dcMetaFile; 2,在view的构造函数中初始化 m_dcMetaFile.Create(); 3,在OnLButtonUp中将所有dc指针更换为m_dcMetaFile 4,在OnDraw中以前的绘图语句注释掉,添加以下 HMETAFILE hmetaFile; hmetaFile=m_dcMetaFile.Close(); pDC->PlayMetaFile(hmetaFile);//播放原文件 m_dcMetaFile.Create();//新创建原文件,准备原文件设备上下文 m_dcMetaFile.PlayMetaFile(hmetaFile);//把前面的原文件播放一遍,避免丢失。相当于保存以前的绘图命令 DeleteMetaFile(hmetaFile);//播放结束,释放原文件句柄 5,保存 copyMetaFile 1)保存语句 HMETAFILE hmetaFile; hmetaFile=m_dcMetaFile.Close(); CopyMetaFile(hmetaFile,"meta.wmf"); m_dcMetaFile.Create(); DeleteMetaFile(hmetaFile); 2)打开图形 HMETAFILE hmetaFile; hmetaFile=GetMetaFile("meta.wmf"); m_dcMetaFile.PlayMetaFile(hmetaFile); DeleteMetaFile(hmetaFile); Invalidate(); 利用兼容DC保存图形 1,在View中定义变量 CDC m_dcCompatible; 2,在OnLButtonUp中添加 if(!m_dcCompatible.m_hDC) { m_dcCompatible.CreateCompatibleDC(&dc); CRect rect; GetClientRect(&rect); CBitmap bitmap; //用当前客户区大小来创建兼容位图 bitmap.CreateCompatibleBitmap(&dc,rect.Width(),rect.Height()); //把兼容位图选入兼容DC中。此位图bitmap可以是普通位图,也可以是兼容位图 m_dcCompatible.SelectObject(&bitmap); m_dcCompatible.BitBlt(0,0,rect.Width(),rect.Height(),&dc,0,0,SRCCOPY); m_dcCompatible.SelectObject(pBrush); } 3,把OnLButtonUp中dc指针都换成m_dcCompatible 已经建立好的基类为CView的视类,改变为有滚动条的视类,即CScroolView步骤 1,将CXXXView的.h and .cpp文件中CView统统用CScrollView替换,注意类定义继承的地方 2,添加OnInitialUpdate,在其中添加滚动条的初始化。不做这一步的话,将运行出错 SetScrollSizes(MM_TEXT,CSize(800,600)); 第十二章 文件操作 我们假设您已经: 1,知道如何创建一个单文档的App Wizard 2,知道C++ 类、函数重载等简单知识 3,知道如何给View类或者Doc文档添加成员变量 4,会用MFC的IDE调试工具最好,那么本文的程序您可以copy去调试 5,知道如何为某个框架类添加虚函数或消息处理函数 1, 指向常量的指针&&指针常量 Char ch[5]=”lisi”; Const char * pStr=ch; const char *等同char const * Char * const *pStr=ch; 指针是常量,指针不可更改,其内容可更改 2, 读写 文件读取操作 FILE *pFile=fopen("1.txt","r"); char ch[100]="0"; //数组被赋值为全零 memset(ch,0,100);//等同于上一句? //char ch[100]; //如果不把数组赋零,也可以在写入文件中多写一个空字符 如fwrite("I Love You",1,strlen("I Love You")+1,pFile); //memset(ch,0,100); 把数组赋为全零的另一种方法。 fread(ch,1,100,pFile); fflush(pFile); 3, 获取文件大小 fseek(pFile,0,SEEK_END); //把文件指针移到文件末尾 int n=ftell(pFile); //得到文件长度 rewind(pFile); //把指针移回文件头 fseek(pFile,0,SEEK_BEGIN) pbuf=new char[n+1]; pbuf[n]=0; //文件尾加\0作为文件结束符 fread(pbuf,1,n,pFile); 4, 文本和二进制方式。读取和写入的保持一致 文本:写入, 换行(10)è回车--换行(ASCII为13、10) 读取, 回车--换行(ASCII 13、10)è换行(10) 二进制:将数据在内存中的存储形式原样输出到文件中 不管是文本文件还是二进制文件,都可以用文本方式或者二进制方式中的任意一种打开 5, 字符和数字 FILE *pFile=fopen("2.txt","w"); int i=98341; //非要使他可用,可增加itoa(i,ch,10); fwrite(&i,4,1,pFile); 6, C++中文件操作 需要加头文件#include "fstream.h" ofstream os("3.txt"); os.write("I love you!",strlen("I love you!")); os.close(); 读文件: ifstream ifs("3.txt",ios::in); char ch[100]="0"; memset(ch,0,100) ifs.read(ch,100); ifs.close(); //循环读取文件每一行 While(!ifs.getline(ch,100).eof()) {//do something with data in buffer ch } 下一次重新getline之前,需要 ifs.clear()清除eof标志 7, Win32API函数存取文件 (1)写入 HANDLE hfile; hfile=CreateFile("6.txt",GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); DWORD dwWrites; WriteFile(hfile,"I Love You!(5)",strlen("I Love You!(5)"),&dwWrites,NULL); CloseHandle(hfile); (2)读取 HANDLE hfile; hfile=CreateFile("6.txt",GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); char ch[100]="0"; //如果只写char ch[dwRead-1];可以在之后用ch[dwRead]=0设结束符 DWORD dwRead; ReadFile(hfile,ch,100,&dwRead,NULL); ch[dwRead]=0; CloseHandle(hfile); 8, SDK方法 (1)写入: CFile file("7.txt",CFile::modeCreate | CFile::modeWrite); file.Write("I Love You 1000",strlen("I Love You 1000")); file.Close(); (2)读取: CFile file("7.txt",CFile::modeRead); char *pBuf; DWORD i=file.GetLength(); pBuf=new char[i+1]; file.Read(pBuf,i); pBuf[i]=0; file.Close(); 9, 构造文件对话框,存取文件方法 (1)写入: CFileDialog fileDlg(FALSE); fileDlg.m_ofn.lpstrTitle="我的文件保存对话框"; fileDlg.m_ofn.lpstrFilter="Text Files(*.txt)\0*.txt\0All Files(*.*)\0*.*\0\0"; //注意lpstrFilter的构造:每个段落后边都要加\0,末尾要加两个\0,括号里的只是显示,实//现在紧跟着的\0后边,此过滤器只为过滤可见文件使用,并不能按所见格式保存。 fileDlg.m_ofn.lpstrDefExt="txt"; if(IDOK==fileDlg.DoModal()) { CFile file(fileDlg.GetFileName(),CFile::modeCreate | CFile::modeWrite); file.Write("I Love You 1000",strlen("I Love You 1000")); file.Close(); } (2)读取 CFileDialog fileDlg(TRUE); fileDlg.m_ofn.lpstrTitle="我的文件打开对话框"; fileDlg.m_ofn.lpstrFilter="Text Files(*.txt)\0*.txt\0All Files(*.*)\0*.*\0\0"; if(IDOK==fileDlg.DoModal()) { CFile file(fileDlg.GetFileName(),CFile::modeRead); char *pbuf; DWORD i=file.GetLength(); pbuf=new char[i+1]; //动态建立缓冲区,值得学习 pbuf[i]=0; file.Read(pbuf,i); MessageBox(pbuf); file.Close(); } 10, 读写配置文件 CXXXApp::InitInstance(){ // 写在SetRegistryKey(_T("Local AppWizard-Generated Applications"));之后(也可以重新设置表项) ::WriteProfileString("songpeng","sp","song");用来在C:\WINDOWS\win.ini中写入数据。一方面为了兼容十六位程序,另一方面提高程序运行速度 //在win32中为[HKEY_CURRENT_USER]è[Software]è[Local Appwizard-Generated Applications]è[File] CString str; ::GetProfileString("songpeng","sp","peng",str.GetBuffer(100),100); } 11, 读写注册表 读写配置文件的函数WriteProfileString(),GetProfileString()在win32下自动成为注册表的读写。 SetRegistryKey(_T("Local AppWizard-Generated Applications"));用来在注册表的HKEY_CURRENT_USER->Software下增加主键Local AppWizard-Generated Applications 子键及值由WriteProfileString("songpeng","sp","song");增加 (1)写入注册表: HKEY hkey; RegCreateKey(HKEY_LOCAL_MACHINE,"Software\\MyItem\\Name",&hkey); RegSetValue(hkey,NULL,REG_SZ,"song",strlen("song")); RegSetValueEx(hKey,”age”,0,REG_DWORD,(CONST BYTE*)&dwAge,4); RegCloseKey(hkey); (2)读取注册表 注意要先获取字段大小 LONG lValue; RegQueryValue(HKEY_LOCAL_MACHINE,"Software\\MyItem\\Name",NULL,&lValue); char *buf=new char[lValue]; //注意建立缓冲区方法 RegQueryValue(HKEY_LOCAL_MACHINE,"Software\\ MyItem\\Name",buf,&lValue); LONG RegQueryValue( HKEY hKey, // handle to key to query LPCTSTR lpSubKey, // name of subkey to query LPTSTR lpValue, // buffer for returned string PLONG lpcbValue // receives size of returned string ); If lpValue is NULL, and lpcbValue is non-NULL, the function returns ERROR_SUCCESS, and stores the size of the data, in bytes, in the variable pointed to by lpcbValue. This lets an application determine the best way to allocate a buffer for the value's data. 所以,我们要调用两次RegQueryValue,第一次查询键值长度,第二次获得键值 HKEY hkey; RegOpenKey(HKEY_LOCAL_MACHINE, "Software\\MyItem\\Name",&hkey); //打开主键 DWORD dwType; DWORD dwAge; DWORD dwValue; RegQueryValueEx(hkey,"age",0,&dwType,(LPBYTE)&dwAge,&dwValue); 其他函数: RegDeleteKey(); RegDeleteValue();等 第十三章 文档与串行化 我们假设您已经: 1,知道如何创建一个单文档的App Wizard 2,知道C++ 类、函数重载等简单知识 3,知道如何给View类或者Doc文档添加成员变量 4,会用MFC的IDE调试工具最好,那么本文的程序您可以copy去调试 5,知道如何为某个框架类添加虚函数或消息处理函数 本课内容: 1, 首先讲解了一下什么是串行化, 2, 然后建立一个可串行化的类,并串行化之 一、CArchive 和串行化 把对象(一个类)保存在永久性媒质上,比如磁盘。然后读取时,可以在内存中重新构建这个对象。把对象存储在磁盘的过程称为“串行化”。 一个Archive和一个文件相关,并且允许带缓冲的写和从存储器读出数据。 创建CArchive之前必须创一个一个CFile,必须保证CArchive的load/store状态和文件的打开模式相一致。一个文件只能有一个活动的CArchive。 当你创建一个CArchive对象时,和CFile或者其派生类关联来表示一个打开的文件。也可以指定CArchive是否是用来load或store。Archive不仅可以处理基本类型,也可以处理CObject派生 CArchive的使用例子。 写入: CFile file("1.txt",CFile::modeCreate | CFile::modeWrite); CArchive ar(&file,CArchive::store); int i=4; float b=1.3f; //C默认用float定义而不加f的为double型 CString str="SongPeng"; ar<>i>>b>>str; strRestult.Format("%d %f %s",i,b,str); MessageBox(strRestult); Note:读取和保存的格式、顺序要完全一致 二、 文档调用顺序 OnNewDocument()是虚函数。在程序运行之初构造文档,或是新建文档。 可以用GetDocString()函数获取IDR_MAINFRAME字符串的内容 修改Doc标题: (1) 可以在其中设置文档标题SetTitle("XXXX"), (2) 在String Table中修改标题字段 (3) 在新建工程的第四部advance选项中也能进行修改 点击“新建”后,先调用OnFileNew()函数,在OnFileNew中再调用OnNewDocument(). Void CwinApp::OnFIleNew(){ If(m_pDocManager != NULL)m_pDocManager->OnFileNew(); } OnNewDocument()函数内部调用过程: 1, voidCWinApp::OnFileNew()中调用CDocManager*类型的成员变量m_pDicManager的OnFileNew函数: m_pDicManager->OnFileNew() 2, 在CDocManager::OnFileNew()中从文档链表取出文档指针。 (在这个OnFileNew中调用OpenDocumentFile(),以下两步都是本函数内语句) pTemplate = (CDocTemplate*)m_templateList.GetHead(); 执行pTemplate->OpenDocumentFile(NULL);语句 //调用单文档的OpenDocumentFile() 3, CDocument* CSingleDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName, BOOL bMakeVisible) (在OpenDocumentFile中创建文档、视类、和框架类对象) pDocument = CreateNewDocument(); pFrame = CreateNewFrame(pDocument, NULL); if (!pDocument->OnNewDocument()) BOOL CGraphicDoc::OnNewDocument() 三、 串行化Serialize(CArchive &ar) 保存和读取数据的函数。点击“打开”或者“保存”菜单,最先调用此函数 执行例子: if(ar.IsStoring()) {ar<>m_nDrawType>>m_pOrigin>>m_pEnd;} 我们文档类中调用Serialize保存一个可串行化的类的数据时实际上是利用了这个对象本身的Serialize函数,这个对象需要什么对象,都需要在你编写可串行化的类时去确定 OnFileOpen函数调用内部过程 void CWinApp::OnFileOpen()中调用 void CDocManager::OnFileOpen()中调用DoPromptFileName 在其中获得打开文件对话框, 然后回到CDocManager::OnFileOpen()中,再调用 AfxGetAp()->OpenDocumentFile();在其中初始化pOpenDocument指针,注意其中执行match=pTemplate->MatchDocType()查看是否有打开文档。如果文档已经打开,就不会调用Serialize()函数 生成可串行化的类的五个步骤: A serializable class usually has a Serialize member function, and it usually uses the DECLARE_SERIAL and IMPLEMENT_SERIAL macros, as described under class CObject. 分为五步: 1 . Deriving your class from CObject (or from some class derived from CObject).(即生成可串行化的类) 2 . Overriding the Serialize member function. 3 . Using the DECLARE_SERIAL macro in the class declaration. 4 . Defining a constructor that takes no arguments. 5 . Using the IMPLEMENT_SERIAL macro in the implementation file for your class. IMPLEMENT_SERIAL(CGraph,CObject,1),1为版本号,保存和读取时版本号必须相同一个文档类对象能和多个视类对象相关,一个视类对象只和一个文档类对象相关. CPtr类和CObArray类很相似,只是CObArray类的add增加的是CObject *指针类型。 类存入文件的例子 void CGraphicDoc::Serialize(CArchive& ar){ POSITION pos=GetFirstViewPosition();//多文档的时候,有多个view CGraphicView *pView=(CGraphicView *)GetNextView(pos); if (ar.IsStoring()){ int nCount=pView->m_obArray.GetSize(); ar<m_obArray.GetAt(i);} } else { int nCount;ar>>nCount;//读出数目 CGraph *pGraph; for(int i=0;i>pGraph;pView->m_obArray.Add(pGraph);} } } 类对象存入数组 CGraph *pGraph=new CGraph(m_nDrawType,m_pOrigin,point); m_obArray.Add(pGraph); 一个文档来可以有多个相关的视类对象,而一个视类对象只能有一个文档类,要获得一个视类对象指针,就需要获得第一个视类对象在链表中的指针。然后通过POSITION值,调用GetNextView()不断获取视类对象 CDocument* CSingleDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName, BOOL bMakeVisible)--->if (pDocument == NULL)--->if (pFrame == NULL)---> if (lpszPathName == NULL)--->if (!pDocument->OnOpenDocument(lpszPathName))---> BOOL CDocument::OnOpenDocument(LPCTSTR lpszPathName)----> Serialize(loadArchive);---->void CGraphicDoc::Serialize(CArchive& ar) 第十四章 网络编程 网络编程的内容和MFC关联并不大,并不是MFC架构的主要内容,所以我记得比较简略。 一、ISO/OSI七层参考模型 二、套接字(socket)的一些文字描述 套接字存在于通信区域中。通信区域也叫地址族,它是一个抽象的概念,主要用于将通过套接字通信的进程的共有特性综合在一起。套接字通常只与同一区域的套接字交换数据(也有可能跨区域通信,但这支在执行了某种转换进程后才能实现)。Windows Sockets只支持一个通信区域:网际域(AF_INET),这个域被使用网际协议簇通信的进程使用。 网络字节顺序不同的计算机存放多字节值的顺序不同,有的机器在起始地址存放低字节(低位先存),有的机器在起始地址存放高字节(高位先存)。基于Intel的CPU,即我们通常使用的PC机采用的是低位先存。为保证数据的正确性,在网络协议中需要制定网络字节顺序。TCP/IP协议使用16位整数和32位整数的高位先存格式。 网间进程通信完全是异步的,相互通信的进程间既不存在父子关系,又不共享内存缓冲区,因此需要一种机制为希望通信的进程间建立联系,为二者的数据交换提供同步,这就是基于客户机/服务器模式的TCP/IP。 套接字的类型 流式套接字(SOCK_STREAM) 提供面向连接,可靠的数据传输服务,数据无差错,无重复的发送,且按发送顺序接收 数据报套接字(SOCK_DGRAM) 提供无连接服务。数据报以独立包形式发送,不提供错误保证,数据可能丢失或重复,并且接收顺序混乱。基于UDP原始套接字(SOCK_RAW)。 基于TCP的socket编程服务器和客户端进行通信都使用send/recv 基于UDP的socket编程服务器端为接收端,客户端为发送端。发送数据为sendto,接收数据为recvfrom 三、一些结构定义和函数 (一) 、3个结构定义: 1, SOCKET socket ( int af, //指定地址族,对于TCP/IP只能是AF_INET(PF_INET) int type, //SOCK_STREAM,SOCK_DGRAM int protocol //推荐为零,可自动选择协议 ); 2, struct sockaddr_in{ short sin_family; unsigned short sin_port; struct in_addr sin_addr; char sin_zero[8]; }; 3, struct in_addr { union { struct{unsigned char s_b1,s_b2,s_b3,s_b4;} S_un_b; struct {unsigned short s_w1,s_w2;} S_un_w; unsigned long S_addr; } S_un; }; 4, struct sockaddr {u_short sa_family;char sa_data[14];}; (二) 4个函数定义 1, SOCKET accept ( SOCKET s, struct sockaddr FAR* addr, int FAR* addrlen //必须在传入一个addrlen之前为它赋初始值,否则调用失败 ); //int len=sizeof(SOCKADDR); 2, unsigned long inet_addr ( const char FAR * cp );//用来把IP地址转化为ULONG类型,用于IN_ADDR结构 3, char FAR * inet_ntoa ( struct in_addr in );//返回一个点分十进制地址值 4, int bind ( SOCKET s, const struct sockaddr FAR* name, int namelen); 在为我们的网络程序指定端口号时,我们要用1024以上的端口. 两个类型转换函数: (1) htonl把一个u_long类型从主机字节序转换为网络字节序 (2) htons把一个u_short类型从主机字节序转换为网络字节序 四、TCP聊天程序服务器端程序 #include #include main(){ WORD wVersionRequested; WSADATA wsaData; wVersionRequested = MAKEWORD( 1, 1); int err = WSAStartup( wVersionRequested, &wsaData ); if ( err != 0 ) { return;} if ( LOBYTE( wsaData.wVersion ) != 1 || HIBYTE( wsaData.wVersion ) != 1 ) { WSACleanup( );return; } SOCKET sockSrv=socket(AF_INET,SOCK_STREAM,0); //第三个参数为零表示自动选择协议 SOCKADDR_IN addrSrv; //定义一个地址结构体的变量 addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY); addrSrv.sin_family=AF_INET; addrSrv.sin_port=htons(6000);//htons把一个u_short类型从主机字节序转换为网络字节序 bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR)); listen(sockSrv,5); SOCKADDR_IN addrClient; int len=sizeof(SOCKADDR); while(1) { SOCKET sockConn=accept(sockSrv,(SOCKADDR*)&addrClient,&len); char sendBuf[100]; sprintf(sendBuf,"Welcome %s to here",inet_ntoa(addrClient.sin_addr)); send(sockConn,sendBuf,strlen(sendBuf)+1,0); char recvBuf[100]; recv(sockConn,recvBuf,100,0); printf("%s\n",recvBuf); closesocket(sockConn); } } 要在控制台使用套接字,需要加入头文件 #include 和库函数ws2_32.lib 要链接一个动态链接库,我们要在VC++菜单栏中选择Project--->Settings--->Link,在其中的Object/Library modules中先打入一个空格,再添加库函数ws2_32.lib 五、TCP聊天程序客户端程序 #include #include main(){ WORD wVersionRequested; WSADATA wsaData; wVersionRequested = MAKEWORD( 1, 1); int err = WSAStartup( wVersionRequested, &wsaData ); if ( err != 0 ) { return; } if ( LOBYTE( wsaData.wVersion ) != 1 || HIBYTE( wsaData.wVersion ) != 1 ) {WSACleanup( );return} SOCKET sockClient=socket(AF_INET,SOCK_STREAM,0); SOCKADDR_IN addrSrv; addrSrv.sin_addr.S_un.S_addr="127.0.0.1"; //本地回路地址,不管本地主机上有没有网卡,都可以测试网络 TCP和UDP编程代码大致相同,不同之处在于,TCP使用send/recv;UDP使用sendto/recvfrom; sendto(sockClient,"Hello!",strlen("Hello!")+1,0,(SOCKADDR*)&addrSrv, sizeof(SOCKADDR)); recvfrom(sockSrv,recvBuf,100,0,(SOCKADDR*)&addrClient,&len); 六、UDP的聊天程序服务器版: #include #include void main() { WORD wVersionRequested; WSADATA wsaData; wVersionRequested = MAKEWORD( 1, 1); int err; = WSAStartup( wVersionRequested, &wsaData ); if ( err != 0 ){ return; } if ( LOBYTE( wsaData.wVersion ) != 1 ||HIBYTE( wsaData.wVersion ) != 1 ) { WSACleanup( ); return; } SOCKET sockSrv=socket(AF_INET,SOCK_DGRAM,0); SOCKADDR_IN addrSrv; addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY); addrSrv.sin_family=AF_INET; addrSrv.sin_port=htons(6000); bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR)); char sendBuf[100],recvBuf[100],temp[200]; SOCKADDR_IN addrClent; int len=sizeof(SOCKADDR); while(1) { recvfrom(sockSrv,recvBuf,100,0,(SOCKADDR*)&addrClent,&len); if('q'==recvBuf[0]) { sendto(sockSrv,"q",strlen("q")+1,0,(SOCKADDR*)&addrClent,len); printf("chat end!\n"); break; } sprintf(temp,"%s:%s",inet_ntoa(addrClent.sin_addr),recvBuf); printf("%s\n",temp); printf("please input data:\n"); gets(sendBuf); sendto(sockSrv,sendBuf,strlen(sendBuf)+1,0,(SOCKADDR*)&addrClent,len); } closesocket(sockSrv); WSACleanup(); } 七、UDP聊天程序客户机版: #include #include void main() { WORD wVersionRequested; WSADATA wsaData; int err; wVersionRequested = MAKEWORD( 1, 1); err = WSAStartup( wVersionRequested, &wsaData ); if ( err != 0 ) { return; } if ( LOBYTE( wsaData.wVersion ) != 1 || HIBYTE( wsaData.wVersion ) != 1 ) { WSACleanup( ); return; } SOCKET sockClient=socket(AF_INET,SOCK_DGRAM,0); SOCKADDR_IN addrSrv; addrSrv.sin_addr.S_un.S_addr=inet_addr("127.0.0.1"); addrSrv.sin_family=AF_INET; addrSrv.sin_port=htons(6000); char sendBuf[100]; char recvBuf[100]; char temp[200]; int len=sizeof(SOCKADDR); while(1) { printf("please input data\n"); gets(sendBuf); sendto(sockClient,sendBuf,strlen(sendBuf)+1,0,(SOCKADDR*)&addrSrv, len); recvfrom(sockClient,recvBuf,100,0,(SOCKADDR*)&addrSrv, &len); if('q'==recvBuf[0]) { sendto(sockClient,"q",strlen("q")+1,0,(SOCKADDR*)&addrSrv,len); printf("chat end!\n"); break; } sprintf(temp,"%s:%s",inet_ntoa(addrSrv.sin_addr),recvBuf); printf("%s\n",temp); } closesocket(sockClient); WSACleanup(); } 记着要加载库函数ws2_32.lib 启动顺序应遵循先服务器后客户机,否则容易出错。 发送字符时应该多加一个空字符作为结束字符。 PAGE 4
本文档为【C++笔记孙鑫VC学习笔记孙鑫VC学习笔记】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
is_431987
暂无简介~
格式:doc
大小:300KB
软件:Word
页数:69
分类:互联网
上传时间:2018-09-03
浏览量:21