孙鑫VC学习笔记[1]
孙鑫VC++视频笔记列表(全)
简介
第一课Windows程序内部运行机制
第二课MFC程序框架的剖析
第三课MFC消息映射机制和DC的获取
第四课简单字处理软件
第五课菜单编程
第六课对话框编程1
第七课对话框编程2
第八课MFC中各类指针的获取
第九课应用程序外观修改
第十课图形绘制与通用对话框
第十一课图形保存和重绘
第十二课文件操作
第十三课文档和串行化
第十四课网络编程
第十五课多线程和简单聊天室制作
第十六课线程同步与异步套接字编程
第十七课进程间通信
第十八课ActiveX控件
第十九课动态链接库
第二十课Hook钩子函数
第0章简介
Lesson1:Windows程序运行原理及程序编写流程,窗口产生过程,句柄原理,消息队列,回调函数,窗口关闭与应用程序退出的工作关系,使用VC++的若干小技巧,stdcall与cdecl 调用规范的比较,初学者常犯错误及注意事项。
Lesson2:C++经典语法与应用,类的编写与应用,构造与析构函数,函数的重载,类的继承,函数覆盖,基类与派生类的构造函数、析构函数先后调用顺序,如何在派生类构造函数中向基类的构造函数传递参数,this成员变量,类型转换的内幕,虚拟函数与多态性,引用和指针变量的区别与共同处。VC工程的编译原理与过程,将工程中不同的类拆分到不同的文件中,每一个类由一个.h和.cpp文件共同完成,头文件重复定义问题的解决,培养了学员良好的编程习惯,也为以后分析MFC AppWizard生成的工程奠定了良好基础。
Lesson3:讲述MFC AppWizard的原理与MFC程序框架的剖析。AppWizard是一个源代码生成工具,是计算机辅助程序设计工具,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改变窗口的回调函数,通过改变文本框的默认回车处理方式进行演示。实现多个输入文本框间通过回车逐一向下传递焦点的另一种巧妙方法(用缺省按钮来处理)。
Lesson8:逃跑按钮的巧妙实现。如何制作属性页对话框和向导对话框,融合讲解组合框(如何调整组合框的大小)、列表框、单选按钮、复选按钮等常用对话框控件的多种使用方法。如何限制用户在不满足设定的条件时切换到其他属性页和向导页。
Lesson9:如何修改MFC AppWizard向导生成的框架程序的外观和大小,修改图标、光标、背景的三种方法。如何增加和删除工具栏按钮,如何给应用程序增加工具栏,如何显示和隐藏工具栏。定制状态栏,在状态栏中添加时钟显示,CTime类及其用法。在状态栏中添加进度条(主窗口产生后立即产生进度条的巧妙思想,不能在OnCreate函数中直接处理,要用到自定义消息的方法)。鼠标坐标显示,在CView中获取状态栏对象的几种方式。如何
为应用程序添加启动画面。
Lesson10:图形的绘制,如何使用自定义画笔(颜色,线宽,线型)。如何为程序中添加选项菜单和选项设置对话框,如何使用标准颜色对话框,如何使用字体对话框,在选项对话框中实现预览功能。实现选项对话框和窗口类中的数据交换。如何改变对话框和控件的背景色,如何改变控件的文本颜色,对按钮控件的特殊处理。如何在窗口中显示一幅位图。
Lesson11:如何让CDC上输出的文字、图形具有保持功能,集合类CPtrArray的使用,CPaintDC与CClientDC的区别与应用,OnPaint与OnDraw在CView中的关系及实现内幕,滚动窗口的实现,坐标空间,映射方式,设备坐标与逻辑坐标的转换。元文件设备描述表的使用,如何利用兼容DC实现图形的保存和再现。
Lesson12:const char*与char*const的区别。C语言对文件读写的支持,FILE指针;文本文件和二进制文件的区别。用文本方式读写文件和以二进制方式读写文件的注意事项。C++对文件读写的支持,ofstream和ifstream的用法。Win32SDK对文件读写的支持,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::In itInstance()函数中通过文档模板将文档类,视类,框架类的有机组织一起。语句如下:
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),获取文字框大小备用
CSiz e 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;
}
CSiz e 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);
CSiz e 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)->Enable MenuItem(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中添加如下