1.控件
windows OS 提供了大量的标准控件,每种控件都有一个MFC类与之对应。
visual c++中,可以使用的控件可以分为三类:
Windows标准控件
Activex控件
其他MFC控件类
创建windows标准控件有两种方法:静态创建和动态创建。
控件通过向父窗口发送控件通知消息来表明发生了某种事件。在MFC中,控件消息由按钮(BN_)、编辑框(EN_)、组合框(CBN_)、列表框(LBN_)等来产生,在消息名前加上ON_即构成宏名。
2、定时器消息:WM_TIMER
当我们为一个窗口设置了定时器资源之后,系统就会按规定的时间间隔向窗口发送WM_TIMER消息,在这个消息中就可以处理一些需要定期处理的事情。
最后要指出的一点是,在WINDOWS环境下,消息的来源是多方面的,最常见的是用户的操作产生消息,系统在必要的时候也会向程序发送系统消息,其他在运行中的程序也可以向程序发送消息。此外,在程序的内部,也可以根据需要在适当的时候主动产生消息,比如主动产生WM_PAINT消息以实现需要的重画功能。
3、设备描述表
设备描述表也称为设备上下文,在windows环境中,当需要对一个对象,如打印机,屏幕,窗口等进行输出时,就必须先获取这个对象的设备描述表,然后通过这个设备描述表来进行输出。使用设备描述表带来的最大的好处就是输出格式的一致性,因为输出不再是直接针对具体的设备,而是通过统一格式的设备描述表间接地实现。
4、
virtual void DoDataExchange(CDataExchange* pDX); //DDX/DDV support
DoDataExchange函数就是对话框类和对话框资源进行DDX数据交换的函数。在对话框初始化的时候或者在程序中调用UpdateData()函数的时候,这个函数将会被调用。DDX_TEXT这个函数可以处理多种类型的数据成员变量与控件资源之间的数据交换。这中间包括int,uint,long,DWORD,CString,float,double等。PDX这个参数是一个指向一个 CDataExchange对象的指针通过它我们可以设置进行数据交换的方法。比方说:数据交换的方向。这段代码就可以通过PDX的这个标志志判断数据交换的方向是从变量到控件还是从控件到变量,然后进行不同的处理。进行数据交换之后,程序当中就可以通过成员变量来使用用户输入的数据了。
5、设备上下文工作原理
在绝大多数的WINDOWS应用都需要在屏幕上显示自己的数据。由于WINDOWS是一个设备无关的操作系统,所以任何向屏幕上进行输出的功能都要间接地通一个叫做设备上下文(device context)的对象来完成。我们向设备上下文提出屏幕输出的要求,然后由WINDOWS自己来调用具体的输出设备的驱动程序来完成实际的输出工作。围绕设备上下文,MFC提供了一系列与其配合使用的绘图对象,这其中包括画笔对象、刷子对象以及字体对象等等。它们的工作模型是这样的:首先对设备上下文对象——我们简称为DC对象——进行设置,然后选择进行屏幕输出所需要的工具,最后用DC对象的输出函数绘制图形。屏幕输出的目标一般都是窗口的客户区,它是一个万能的输出区域,可以接受无论是文本、位图、还是其他类型的数据(比方说OLE对象)。
6、
下面这段程序的意思是为将要输出的文本选择字体。
LOGFONT logfont;
memset(&logfont, 0, sizeof(logfont));
logfont.lfWeight = 50;
logfont.lfHeight = 50;
lstrcpy(logfont.lfFaceName, "黑体");
nowFont.CreateFontIndirect(&logfont);
dc.SelectObject(&nowFont);
7、有关屏幕映射方式
在一般的情况之下,我们都以像素作为绘图的单位,我们称之为设备坐标。我们在进行绘图操作的时候,不可避免的要用到设备坐标系。
WINDOWS提供了几种映射方式,或称坐标系。可以通过它们来和设备上下文相联系。比方说不管是什么样的显示设备,如果我们需要在上面显示一个2英寸高,2英寸宽的矩形,该怎样处理呢?这就要依赖于我们所设定的坐标系。如果我们指定了MM_TEXT 方式,这时坐标原点就位于屏幕的左上角,X轴和Y轴的方向分别指向我们面对屏幕的右方和下方,它的绘图单位是像素,如果一英寸对应72个像素的话,我们就需要这样绘制这个矩形:
DC.Rectangle(CRect( 0,0,72*2,72*2));
所以我们如果我们指定了MM_LOENGLISH 方式,那么一个绘图单位就是百分之一英寸,坐标原点仍然位于屏幕的左上角,但是X轴和Y轴的方向恰好和MM_TEXT方式下的轴方向相反,同样完成绘制上面提到的矩形的工作,我们就需要写出这样的代码:
DC.Rectangle(CRect(0,0,200,_200));
可见,坐标系的选择对我们编写程序有很大的影响。
此外,在有些时候,我们需要在几个不同的坐标系下面工作,那么还需要在进行在这些坐标系之间的转换工作。所以 ,我们有必要在这里详细介绍以下WINDOWS的坐标映射方法。
一般来说,最常用的就是WM_TEXT方式。在WM_TEXT坐标方式下面,坐标被映射到了像素,X的值向右方递增,Y的值向下递增,并且可以通过调用CDC的SetViewpotOrg函数来改变坐标原点。下面的代码把屏幕映射方式设为MM_TEXT方式,并且把坐标原点设在(300,300)处:
DC.SetMapMode(MM_TEXT);
DC.SetViewportOrg(CPoint(300,300));
另外,WINDOWS提供了一组非常重要的比例固定的映射方式,在这些映射方式下面,我们可以改变它的坐标原点,却无法改变它的比例因子。对于MM_LOENGLISH映射方式,我们已经知道它的X值是向右递减的,Y的值是向下递减的,所有的固定比例的映射方式都遵循这一原则。它们的比例因子也各不相同。我们列表如下:
映射方式 | 逻辑单位 |
MM_LOENGLISH | 0.01英寸 |
MM_HIENGLISH | 0.001英寸 |
MM_LOMETRIC | 0.1毫米 |
MM_HIMETRIC | 0.01毫米 |
MM_TWIPS | 1/1440英寸 |
最后一种映射方式MM_TWIPS常常用语打印机,一个’twip’单位相当于1/20个点(一点近似与1/72)英寸。例如,如果指定的MM_TWIPS一个社单位,那么对于12点大小的字模来说,字符的高度为12x20,即240个twip。
除了固定比例的映射方式,WINDOWS还提供了比例可变的映射方式,在这种映射方式下面,我们除了可以改变它们比例因子之外还可以改变比例因子。借助于这样的映射方式,当用户改变窗口的尺寸的时候,绘制的图形的大小也可以根据比例发生相应的变化;同样,当我们翻转某个轴的时候,他们所绘制的图像,也以另外的一个轴为轴心进行翻转。这样的映射方式有两种:MM_ISOTROPIC和 MM_ANIOTROPIC。
在MM_ISOTROPIC方式下,纵横的比例为1:1,换句话说,无论比例因子如何变化,我们画出的图形不会改变自己的形状。但是在MM_ANIOSTROPIC方式下面,X和Y的比例因子可以独立地变化,图形的形状可以发生变化。
我们分析下面这段程序:
void CAView::OnDraw(CDC *pDC)
{
CRect clientDC;
GetClientRect(clientRect);
pDC_>SetMapMode(MM_ANISOTROPIC); //设置映射方式
pDC_>SetWindowExt(1000,1000); //设置x/y坐标的范围(按逻辑单位);设定窗口尺寸
pDC_>SetViewportExt(clientRect.right,_clientRect.bottom); //设置视图端口的X和Y坐标范围(按设备单位);设定视口尺寸
pDC_>SetViewportOrg(clientRect.right/2, clientRect.bottom/2); //设置视图原点
pDC_>Ellipse(CRect(_500, _500, 500, 500));
}
//////视口就是你能看到的那部分,而由于窗口有滚动条,可能有部分不可见,不可见的部分就包括在窗口里面了。 视口<=窗口/////
这段代码的功能是这样的,首先取得窗口客户区矩形的大小,然后用SetWindowExt和SetViewportExt函数设定比例,结果窗口尺寸的大小被设为1000个逻辑单位高和1000个逻辑单位宽,坐标原点被设为窗口的中心,在这样的设置之下,绘制出一个半径为500个逻辑单位的椭圆。
在这里如果将映射方式改变为MM_ISOTROPIC那么就将画出一个圆。圆的直径是窗口举行宽和高的最小值。
下面我们给出逻辑单位到设备单位的公式:
X比例因子 = X视口范围/X窗口范围
Y比例因子 = Y视口范围/Y窗口范围
设备X = 逻辑X *X比例因子 + X坐标原点偏移
设备Y = 逻辑Y *Y比例因子 + Y坐标原点偏移
当我们设定了设备上下文的映射方式之后,就可以直接使用逻辑坐标作为其参数了,但是从WM_MOUSEMOVE消息所获得的鼠标的坐标值是设备坐标。许多其他的MFC库函数,尤其是类CRect的成员函数,只接受设备坐标。所以我们有时要利用CDC的LPtoDP和DPtoLP在逻辑坐标和设备坐标之间进行转换的工作。
下面我们列出进行坐标映射工作的时候所要遵循的一些规则:
- 可以认为CDC的所有成员函数都以逻辑坐标作为参数,但和CRect有关的函数例外。
- 可以认为CWnd 的成员函数都以设备坐标作为参数。
- 所有的HIT_TEST操作都应该考虑设备坐标。
- 以逻辑坐标的形式来保存数据,否则用户对窗口进行滚动操作的时候,这个数据就不再有效了。
8、重载Serialize 成员函数
void CLine::Serialize(CArchive & ar)
{
CObject::Serialize(ar);
if (ar.IsStoring())
{
ar<
ar<
}
else
{
ar>>m_x1>>m_y1;
ar>>m_x2>>m_y2;
}
}
首先调用基类的Serialize函数,CObject::Serialize(ar);然后判断是保存数据还是载入数据,然后再根据判断的结果进行实际的存取工作。这里ar就是框架程序传递给序列化函数的归档对象指针。
当调用完基类的序列化函数后,判断ar的状态,当ar.IsStoring()返回真时,这时进行数据保存;当ar.IsStoring()返回非真时,这时CArchive 对象要求读取数据。
9、与文件处理关系密切的类CFile
CFile类用来处理正常文件的I/O操作,它直接供无缓冲的、二进制磁盘输入/输出服务,并且通过其派生类间接支持文本文件和内存文件。因为CFile类是基本上封装在CArchive类之中了,所以我们只对这个类作简单介绍。
CFile类有三个构造函数,其原型如图所示。
virtual BOOL Open( LPCTSTR lpszFileName, UINT nOpenFlags,
CFileException* pError = NULL )
其中:hFile为一个已打开文件的句柄。LpszFileName指定所想要文件的路径的字符串。路径可以是相对的或绝对的。NOpenFlags指共享和存取方式,对于这个标志的说明,我们留到后面专门说明。
CFile类用Open来创建和打开文件。使用Open创建新文件,必须有一个文件名,并且选择一定的打开方式:
CFile对磁盘文件的定点读和写是通过函数Read ,Write和Seek进行的。
virtual UINT Read( void* lpBuf, UINT nCount );
virtual void Write( const void* lpBuf, UINT nCount );
virtual LONG Seek( LONG lOff, UINT nFrom );
函数Read:返回传输给缓冲区的字节数。如果读到文件尾,返回值可能小于nCount 值。LpBuf:指向用户定义的缓冲区的指针,用来接收数据;nCount:要从文件中读出的最大字节数;函数Write:写缓冲区数据到文件中;Seek用于定位文件指针位置,如果所请求的位置合法,则返回距离文件头的新字节偏移。
文件的打开和关闭是相对的,打开一个文件之后,必须把它关闭,文件的关闭是相当简单的,用CFile对象调用Close函数即可。
下面描述文件共享和存取标志。下列标志指定打开文件时可执行的动作。可以用OR来组合下面所列的选项。一个存取许可和一个共享选项是必需的;modeCreate 和modeNoInberit方式是可选的。
- modeCreate指示构造函数创建一个新文件,如果该文件已存在,则该文件截短为0。
- modeRead 打开文件用于读。
- modeReadWrite 打开文件用于读写。
- modeWrite 打开文件用于只写。
- modeNoInberit 阻止文件被子进程继承。
- shareDenyNone 打开文件,不允许其它进程读或写该文件。如果该文件已由其它任何进程用兼容方式打开,则Create将失败。
- shareDenyWrite 打开文件,不允许其它进程写该文件。如果该文件已由其它任何进程用兼容方式或写方式打开,则Create将失败。
- shareDenyRead 打开文件,不允许其它进程读该文件。如果该文件已由其它任何进程用兼容方式或读方式打开,则Create将失败。
- shareExclusive 以独占方式打开文件,不允许其它进程写该文件。如果该文件已用其它读或写方式打开,即使是当前进程打开,则构造也将失败。
- shareCompat 以兼容方式打开文件,允许给定机器上任何进程打开该文件任意次。如果该文件已用其它任何共享方式打开,构造将失败。
- typeText 设置文本方式,对回车换行进行特殊处理,它只用于派生类。
- typeBinary 设置二进制方式,它只用于派生类。
10.路径层
在绘图时,如果希望图的某一部分与其他部分分开处理,就可以利用路径层的独立性。
CDC::GetTextExtent() //Call this member function to compute the width and height of a line of text using the current font to determine the dimensions
例如:
CString str;//declare a cstring object
str.LoadString(IDS_STR_TEXTOUT);//use loadstring to initial the CString object
pDC->TextOut(50,50,str);
pDC->BeginPath();//Opens a path bracket in the device context.
CSize size = pDC->GetTextExtent(str);//retrieved the width and height of the constent string
pDC->Rectangle(50,50,50+size.cx,50+size.cy);
//Closes a path bracket and selects the path defined by the bracket into the device context.
pDC->EndPath();
pDC->SelectClipPath(RGN_DIFF);//there are many different modul ,you can see in the MSDN
pDC->TextOut(100,100,str);//textout the text, just use to compare with the last text which I textout the first time
//draw a hatch
for(int i = 0;i<300;i+=10)
{
pDC->MoveTo(0,i);
pDC->LineTo(300,i);
pDC->MoveTo(i,0);
pDC->LineTo(i,300);
}
在MFC中,路径层主要运用于在窗口中绘图。
学过Photoshop的同学都知道,我们在设计一张海报时,可能会用到多张图片进行合成,而在合成之前是要对每张图片进行各自处理的。这个时候我们就要给每一张图片定制一个它独有的处理空间---路径层。在各个独立的空间---路径层上,我们对每张图片进行处理而互相不受影响。
类似地,MFC中,在一块窗口上我们也可以定制多个路径层并在各个路径层上进行绘图或输出字符的操作。
MFC中,我们利用CDC类提供的成员函数BeginPath()和EndPath()这两个函数来实现一个路径层的创建。