***************************************/
第一章:Microsoft Windows 和 Visual C++
1,Windows应用程序中一定要有WinMain函数,用来完成某些特殊的任务,其中最主要的是创建应用次序的主窗口。
2,DOS下,所有次序的目标模块在创建过程中都被静态连接起来。而Windows允许动态连接,即一些特定结构的库(DLL)可以在运行过程中被装入和连接,并且被多个应用程序共享。
3,项目是一些相互关联的源文件的集合,这些源文件经过编译、连接,然后被组合在一起形成可执行的Wwindows应用程序或DLL。
4,几个重要Developer Studio创建的中间文件:
CLW (支持ClassWizard ) DSP (项目文件)
DSW (工作空间文件) NCB (支持ClassView)
OPT (保持工作空间的配置) PLG (建立日志文件)
5,AppWizard是一个代码生成器,它会按照用户通过对话框指定的特性、类名、及源代码文件名来产生Windows应用程序工作架构。AppWizard所产生的代码只是一些最基本的代码,它完成的功能完全由应用程序的基类所决定。
/**************************************/
第二章:Microsoft基本类库应用程序框架
1,按照惯例,MFC库类名用大写“C”打头。
2,类CMyApp的对象theApp就代表一个应用程序。当运行该应用程序的时候,WINDOWS自动调用程序框架内部的WinMain函数,WinMain函数会去查找该应用程序的全局构造对象(theApp),该对象是有CWinApp类派生类的对象。C++中,全局对象在主程序被运行之前就已经构造好了。
3,当WinMain发现该应用程序对象(theApp)时,会自动调用虚拟成员函数CWinApp::InitInstance(),该函数会进一步调用相应的函数来完成主窗口的构造和显示工作。由于基类CWinApp中不知道我们实际需要的什么样的窗口,所以我们必须在派生出的应用程序类CMyApp中重载InitInstance()函数(CMyApp::InitInstance()),由多态性,当调用CWinApp::InitInstance()时候,会自动转向MyApp::InitInstance().
4,WinMain函数调用完InitInstance()函数后,就调用CWinApp::Run函数,CWinApp::Run函数被隐藏在基类中负责传递应用程序的消息给相应的窗口,从而维护应用程序的运转。
5,关闭应用程序(对单一框架程序来说),首先CMyFrame 对象被删除,然后退出Run,进而退出WinMain,最后删除CMyApp对象(theApp)。
6,一些Windows类库在基类为鼠标事件消息以及其他一些标准消息定义一些虚函数,如果需要,派生类会对这些函数进行重载。
MFC库应用程序框架没有采用虚函数来处理Windows消息,而是通过一些宏来将特定的消息映射到派生类中相应的成员函数上。(原因:P21。C++类对程序中用到的每一个派生类都要求有一张虚函数分发表VTABLE,在BTABLE中每个虚函数都需要有一个4字节的入口项,而不管该函数会不会在派生类中被重载。)
MFC消息控件函数要求提供函数原型,函数体,以及在消息映射中的入口。
7,文档-视图结构将数据从用户对数据的观察中分离出来,这样就允许对同一数据可以有多个视图。
MFC库应用程序中,文档和视图是由C++类的实例来描述的。
文档基类代码通常和FILE OPEN,FILE SAVE菜单项关联,而派生文档类则一般用来完成对文档对象数据的实际读写工作。
视图基类通常表示一个包含于框架窗口中的窗口,而派生视图类则常用来和稳当类相联系,负责应用程序的显示和打印机I/O。
派生视图类及其基类共同处理WINDOWS的消息,而MFC库则协调文档,视图,框架窗口,以及应用程序之间的相互作用关系,这种协调多用虚函数来实现。
文档对象既可以与一次可全部读入内存的磁盘文件相关联,也可以同数据库相关联。
/********************************************/
第三章:从“Hello,world!”着手学习AppWizard
1,视图:从用户角度看,视图是一个普通的窗口;从程序员角度看,试图是一个从MFC库中CView类所派生的类的对象。视图对象的行为完全由类的成员函数和数据成员决定,其中包括派生类的特定成员和基类的继承而来的成员。
2,CEx03aView类最重要的基类是CWnd和CView。CWnd提供CEx03aView的窗口属性,而CView提供它和应用程序框架其它部分之间的联系,特别是和文档及框架窗口之间的联系。
3,视图类中OnDraw成员函数:OnDraw成员函数被设计成一个虚成员函数,每次当视窗需要被重绘时候,应用程序都要调用OnDraw函数。
当用户改变了窗口尺寸,或当前窗口恢复了先前被遮盖的部分,或当前应用程序改变了窗口的数据的时候,窗口都需要被重绘。
当用户改变了窗口尺寸,或窗口需要恢复被遮盖的部分,则应用程序自动去调用OnDraw函数。
但注意,如果程序中某个函数修改了窗口数据,则它必须通过调用视图所继承的Invalidate(InbalidateRect)成员函数来通知Windows,调用Invalidate后会触发对OnDraw函数的调用。
4,WINDOW中不允许直接访问显示硬件,必须通过和窗口相关联的“设备环境”跟显示硬件通讯。MFC中设备环境由CDC类对象来表示。
5,利用Win32 Debug模式 和 Win32 Release模式开发应用程序。
(默认情况不出现BUILD工具栏,可从TOOLS菜单选择Customize项,设置显示Build工具栏,VC6中也可在工具栏上直接点击右键选择Build工具栏)
Win32 Release模式 Win32 Debug模式
源码调试 不能 编译器连接器都允许
MFC诊断宏 不能(定义了NDEBUG) 可以(定义了_DEBUG)
库连接 MFC Release库 MFC Debug库
编译优化 速度优化(学习版不能) 没有优化(快速编译)
我们应在Win32 Debug模式下开发应用程序,然后在发布或交付前用Win32 Release模式重建应用程序。in32 Release模式下建立的EXE文件又小又快。
6,诊断宏TRACE宏对监测程序状态特别有用。这些宏要求在允许追踪的情况下才有效,允许追踪是默认设置。
(问题:TRACE宏的应用事例与运行原理。宏TRACE的输出在在哪显示呢?在output输出框下选择Debug项,调试运行程序即可看到TRACE宏输出。)
7,预编译头文件代表了编译器在源代码特定行上进行‘快照’。在MFC库程序中,快照常紧接在#include "stdafx.h"后进行。
文件StdAfx.h包含了MFC库头文件的#include语句。该文件的内容取决于运行AppWizard时候所做的选择。
文件StdAfx.cpp只包含#include "stdafx.h"语句。StdAfx.cpp文件用来在相应项目的目录下产生预编译头文件(PCH文件)。编译开关/Yc只用于StdAfx.cpp文件,导致产生预编译头文件(PCH),而其它源文件的/Yu编译开关使可以使用一个存在的PCH文件。开关/Fp用来指定专门的PCH文件名,默认情况下为相应目标输出文件目录下与项目同名扩展名为PCH的文件。
(VC6下具体设置参见:[Project Settings属性框] [c/c++]属性页[Project Options]、[Source File Options]项)
(注意:在VC6 [Project Settings属性框]中查看StdAfx.cpp文件[Source File Options]项时候,可以看到[Project Settings, and /Yc"stdafx.h"],这里说明一下,StdAfx.cpp文件的[ /Yc"stdafx.h" ]的设置覆盖了[Project Options]设置中的[/Yu"stdafx.h"])
/******************************************/
第四章:基本事件处理、映射模式和滚动视图
1,消息映射:如在视窗中按下鼠标做键,会自动发送WM_LBUTTONDOWN消息,如果要对这消息有所反应的话,需
//{{AFX_MSG(CEx04aView)
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);//头文件声明消息响应函数原型。
//}}AFX_MSG
DECLARE_MESSAGE_MAP()//头文件
BEGIN_MESSAGE_MAP(CDrawView, CView)//代码文件
//{{AFX_MSG_MAP(CEx04aView)
ON_WM_LBUTTONDOWN()//该消息映射宏将OnLButtonDown函数和应用程序框架联系在一起
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
void CDrawView::OnLButtonDown(UINT nFlags, CPoint point) //代码文件
{
//event processing code here
}
说明:
为了方便ClassWizard,AppWizard生成了一些注释行。它把消息映射函数原型放在两个AFX_MSG“刮弧对”之间,在AFX_MSG_MAP“刮弧对”之间添加消息映射入口,并在代码文件中给出完整的OnLButtonDown成员函数框架。
2,视图中的OnDraw()是根据视图当前状态来绘制图象的,用户的操作可以改变这中状态。在完整的MFC库应用程序中,文档的对象掌握在应用程序(视图)的状态。
3,如果用全程变量来保存视图状态,这将在多视图情况下会遇到麻烦;(待体会)
4,CWnd::InvalidateRect函数能够触发WM_PAINT消息,该消息被窗口类如CView映射后,引起对OnDraw函数调用,从而重绘无效矩形区域。
优化窗口的绘制过程:
1)设置的无效区域越小,重画的速度就越快;
2)执行无效区域外的绘制纯属于浪费时间,OnDraw函数可以调用CDC的成员函数GetClipBox得到无效区域大小。
注意:
OnDraw函数不仅仅响应InValidate*之类的调用,它也会在窗口改变大小和窗口显露时被调用。
5,本章重要介绍函数
PtInRect(..);//测试一个点是否落在某个矩形内。
PtInRegion(..);//判断一个点是否落在椭圆(CRgn对象)内//A region is an elliptical or polygonal area within a window.
GetClientRect(..);//获得客户矩形坐标并保持在CRect对象中
SelectStockObject(..);//选择the predefined stock pens, brushes, or fonts到DC中,函数返回先前的CGdiObject 对象指针。
6,在构造函数中对类数据成员初始化。
CEx04aView::CEx04aView() : m_rectEllipse(0, 0, 200, 200)//注意这种初试化方式,m_rectEllipse是CEx04aView类数据成员CRect类对象
{
m_nColor = GRAY_BRUSH;
}
7,[致WIN32程序员]:
标准的基于WINDOWS应用程序会首先登记一个窗口类(不同于C++类[补:实际是个_WNDCLASS结构体]),同时在处理过程中,还需要对每个类指定窗口过程(WINDOWS PROCEDURE)。每次应用程序调用CreateWindow建立一个窗口时候,都要指定一个窗口类做为参数,这样就把新建立的窗口和窗口过程函数连接起来了。每次Windows给窗口发送消息时候,这个函数就会被调用(补:消息循环中DispatchMessage(&msg)分派消息到窗口的回调函数处理,OS调用窗口回调函数进行处理),以检查用参数传送进来的消息码,并执行适当的代码来处理该消息。
MFC库应用程序框架有一个适用于大多数窗口类型的简单窗口类和窗口过程函数。该窗口过程函数会根据参数传进来的窗口句柄,在MFC的句柄映射表(handle map)中查找,从而得到对应的C++窗口对象指针。然后,该窗口过程函数用MFC运行时类(runtime class)系统来决定窗口对象的C++类。下一步,它从由消息分发映射函数生成的静态表中找到消息处理函数,最后用正确的窗口对象调用消息处理函数。
8,映射模式:
1)MM_TEXT映射模式:坐标被映射到象素,X值向右递增,Y值向下递增。可用它来表示[设备坐标]。
CDC::SetMapMode(..)//设置映射模式
CDC::GetMapMode(..)
CDC::SetViewportOrg(..)//设置视口原点
CDC::GetViewportOrg(..)
CDC::SetWindowOrg (..)//设置屏幕原点
CDC::GetWindowOrg(..)
2)固定比例映射模式(MM_HIENGLISH,MM_HIMETRIC ,MM_LOMETRIC ,MM_LOENGLISH,MM_TWIPS )
固定比例映射模式均X值向右递增,Y值向下递增,它们之间唯一差别是 实际的比例因子。如下:
MM_HIENGLISH Each logical unit is converted to 0.001 inch.
MM_HIMETRIC Each logical unit is converted to 0.01 millimeter.
MM_LOENGLISH Each logical unit is converted to 0.01 inch.
MM_LOMETRIC Each logical unit is converted to 0.1 millimeter.
MM_TWIPS Each logical unit is converted to 1/20 of a point(磅). (Because a point is 1/72 inch, a twip is 1/1440 inch.)
//MM_TWIPS常常用于打印机。
3)可变比例映射模式:(MM_ISOTROPIC ,MM_ANISOTROPIC )
这两种模式用许我们改变它们的比例因子和坐标原点。
应用这两中模式,如用户改变窗口的尺寸,绘制的图形大小也会发生响应的变化
具体如下:
The MM_HIENGLISH, MM_HIMETRIC, MM_LOENGLISH, MM_LOMETRIC, and MM_TWIPS modes are useful for applications that must draw in physically meaningful units (such as inches or millimeters). The MM_ISOTROPIC mode ensures a 1:1 aspect ratio, which is useful when it is important to preserve the exact shape of an image. The MM_ANISOTROPIC mode allows the x- and y-coordinates to be adjusted independently
常一起使用的函数:
SetWindowExt(..)//Sets the x- and y-extents of the window associated with the device context.
SetViewportExt(..)//Sets the x- and y-extents of the viewport of the device context.
注意:
When the following mapping modes are set, calls to SetWindowExt and SetViewportExt functions are ignored:
MM_HIENGLISH,MM_HIMETRIC,MM_LOENGLISH,MM_LOMETRIC,MM_TEXT,MM_TWIPS
When MM_ISOTROPIC mode is set, an application must call the SetWindowExt member function before calling SetViewportExt.
9,坐标变换:(具体参见P54)
许多MFC库函数只能在设备坐标下工作(尤其CRect类成员函数)。可以认为CDC的所有成员函数都一逻辑坐标作参数。可以认为CWnd的成员函数都以设备坐标做参数。(所有在实际窗口上点击获得的坐标都是逻辑坐标)。在设置了设备环境的映射模式及相应的参数以后,CDC的LPtoDP和DPtoLP函数可以用来在逻辑坐标系和设备做表系之间进行转换。
在CView的虚函数OnPrepareDC中设置映射模式要比在OnDraw函数中要好。
//*注意:
CView::OnPrepareDC
virtual void OnPrepareDC( CDC* pDC, CPrintInfo* pInfo = NULL );
应用程序将在调用OnDraw之前调用OnPrepareDC函数。
(OnPrepareDC在为屏幕显示而调用OnDraw函数之前,或在为打印或打印预览每一页而调用OnPrint成员函数之前。)
10,CScrollView支持滚动条的滚动,但不支持键盘的滚动。通过使用CWnd的ScrollWindow和SetViewportOrg函数,CScrollView类允许将视口原点移到窗口中的任何一个位置,甚至包括窗口区域的上部或窗口的原点的左边。
键盘输入是分两步处理的。OS向窗口发送类如WM_KEYDOWN和WM_KEYUP消息时用的是虚拟键盘码,在消息到达窗口之前,被翻译成WM_CHAR消息,该消息带着正常的键码。
利用对WM_KEYDOWN消息进行响应,对按键分别调用OnVScroll就可以使应用程序支持键盘的滚动。
CWnd::OnVScroll
afx_msg void OnVScroll( UINT nSBCode, UINT nPos, CScrollBar* pScrollBar );
////
nSBCode:
SB_BOTTOM Scroll to bottom.
SB_ENDSCROLL End scroll.
SB_LINEDOWN Scroll one line down.
SB_LINEUP Scroll one line up.
SB_PAGEDOWN Scroll one page down.
SB_PAGEUP Scroll one page up.
SB_THUMBPOSITION Scroll to the absolute position. The current position is provided in nPos.
SB_THUMBTRACK Drag scroll box to specified position. The current position is provided in nPos.
SB_TOP Scroll to top.
////
The framework calls this member function(OnVScroll) when the user clicks the window’s vertical scroll bar.
11,CView::OnInitialUpdate
virtual void OnInitialUpdate( );
说明:
OnInitialUpdate是视图窗口完全建立后框架调用的第一个函数。框架在第一次调用OnDraw前会调用OnInitialUpdate。
具体参见下:
Called by the framework after the view is first attached to the document, but before the view is initially displayed. The default implementation of this function calls the OnUpdate member function with no hint information .
12,MFC对140种Windows消息直接直接提供了相应的消息控制函数,当然,我们还可以定义自己的消息和相应的消息控制函数。
五种特殊Windows消息:WM_CREATE,WM_CLOSE,WM_QUERYENDSESSION,WM_DESTROY,WM_NCDESTROY.(具体参见 p62-63页。这两页强烈建议看看,这里简要笔记三个最常用最重要的)
1)WM_CREATE消息:
The WM_CREATE message is sent when an application requests that a window be created by calling the CreateWindowEx or CreateWindow function. The window procedure of the new window receives this message after the window is created, but before the window becomes visible. The message is sent before the CreateWindowEx or CreateWindow function returns.
2)WM_CLOSE消息:
当关闭窗口或父窗口被关闭时,Windows都会发送WM_CLOSE消息。可以重新定义该消息响应函数OnClose来完全控制关闭过程。
3)WM_DESTROY消息:
Windows在发送WM_CLOSE消息之后,紧接着就会发送WM_DESTROY消息(响应这个消息的时候,窗口已经消失但还没有销毁)。
(可以响应这个消息来做一些销毁当前窗口后的一些事情,例如再弹出其它对话框发送其他的消息)
/**********************************************/
第五章:图形设备接口(GDI)、颜色及字体
1,任何时候当程序需要直接在屏幕或打印机上绘图的时候,都需要调用GDI函数,GDI函数包含了一些用于绘制图形、位图以及文本的函数。
2,Windows的设备环境是GDI的关键元素,它代表了物理设备。每一个C++设备环境对象都有与之对应的Windows设备环境,并且通过一个32位类型的HDC句柄来标识。
3,MFC库设备环境类基类CDC包含了绘图所需要的所有成员函数,并且几乎所有派生类只有构造函数和析构函数不同(CMetaFileDC类除外)。
对于显示器来说,常用的派生类有CClientDC 和 CWindowDC,而对其它设备(如打印机或内存缓冲区),则可以构造一个基类CDC的对象。
对于显示器和打印机设备环境对象来说,应用程序框架会直接将句柄附在对象上;而对其它设备环境(如内存设备环境),为了将对象与句柄相联系,在构造完对象之后,还必须调用一个成员函数(进行初试化)。
4,CClientDC类 和 CWindowDC类
CClientDC( CWnd* pWnd );
//Constructs a CClientDC object that accesses the client area of the CWnd pointed to by pWnd. The constructor calls the Windows function GetDC.
CWindowDC( CWnd* pWnd );
//Constructs a CWindowDC object that accesses the entire screen area (both client and nonclient) of the CWnd object pointed to by pWnd. The constructor calls the Windows function GetWindowDC.
如果构造CClientDC对象,则设备环境的映射区域限于客户区域,不能在客户区域外绘图。原点(0,0)在客户区左上角。
《 如果创建CWindowDC对象,则设备环境的映射区域为整个窗口(包括标题栏、状态栏、窗口边框等)。原点(0,0)在整个窗口的左上角。》
注意:
1)视图窗口没有非客户区域.
2)视图窗口覆盖在框架窗口之上。
3)在《》中的内容,是我根据测试所理解的。翻译原文中映射区域是整个显示屏幕,原点(0,0)在整个屏幕的左上角。这显然是不对的。
5,在创建了一个CDC对象后,一定要注意在完成任务后将其删除。(如果用CClientDC 或 CWindowDC来在堆栈中构造一个DC对象,则不需要我们显式的删除它,CClientDC或CWindowDC对象会在它的生命周期结束的时候自动调用析构函数来完成删除工作)
例如:
CDC *pDC=GetDC();
...
RealeaseDC(pDC);
说明:
CDC* GetDC( );//CWnd::GetDC 获得与当前窗口相关联的CDC对象指针(映射窗口客户区域)
int ReleaseDC( CDC* pDC );//CWnd::ReleaseDC 释放与当前窗口相关联的设备环境
注意:
在MFC程序中,千万不能自己添加代码删除作为参数以指针形式传递给OnDraw(CDC* pDC)函数的CDC对象,应用程序会自动控制它的删除。
6,当利用CDC对象绘图的时候,所绘制的图形都要依赖于设备环境的状态:如画笔、画刷、字体等GDI绘图对象 和 当前映射模式的选择等。
可以使用CDC成员函数了设置所需要的饿设备环境状态。如:SelectObject()函数可将GDI对象随时选入设备环境中。
7,CPaintDC类:当需要重写OnPaint函数的时候,就需要使用CPaintDC类。默认的OnPaint函数会使用已经设置好的设备环境来调用OnDraw函数
注意:CPaintDC类的构造函数和析构函数所完成的工作都是针对显示用的。
例:
void CMyView::OnPaint()
{
CPaintDC dc(this);
OnPrepareDC(&dc);
dc.TextOut(0,0,"for the display,not the printer");
OnDraw(&dc);
}
说明:
1)CPaintDC( CWnd* pWnd );
//CPaintDC::CPaintDC Constructs a CPaintDC object, prepares the application window for painting, and stores the PAINTSTRUCT structure in the m_ps member variable.
2)CPaintDC类的构造函数自动调用BeginPaint,而它的析构函数会自动调用EndPaint.
3)CDC* BeginPaint( LPPAINTSTRUCT lpPaint );
//CWnd::BeginPaint Prepares CWnd for painting and fills a PAINTSTRUCT data structure with information about the painting.
void EndPaint( LPPAINTSTRUCT lpPaint );
//CWnd::EndPaint Marks the end of painting in the given window. The EndPaint member function is required for each call to the BeginPaint member function, but only after painting is complete.
问题:
1)在View类中一旦定义了OnPaint()函数,则WM_PAINT消息由OnPaint函数来响应。OnDraw函数不再被调用。原因?
8,GDI对象:
Class Windows handle type
CPen HPEN
CBrush HBRUSH
CFont HFONT
CBitmap HBITMAP
CPalette HPALETTE
CRgn HRGN
说明:
Each graphic-object class in the class library has a constructor that allows you to create graphic objects of that class, which you must then initialize with the appropriate create function, such as CreatePen.
Each graphic-object class in the class library has a cast operator that will cast an MFC object to the associated Windows handle. The resulting handle is valid until the associated object detaches it. Use the object’s Detach member function to detach the handle.
9,CGdiObject类有一个虚析构函数,在其派生类的析构函数将与C++对象相关联的WINDOWS GDI对象删除掉。如果构造了一个CGdiObject派生类对象,则在退出程序之前,必须将其删除掉。
利用CDC类的SelectObject成员函数把自己的GDI对象选进DC的同时,保存原来的GDI对象,当任务完成后,恢复原来的GDI对象,这样就可以将自己的GDI对象的删除掉。
如:
GDIStyle *poldGdiObject=pDC->SelectObject(&newGdiObject);
...//完成任务
pDC->SelectObject(poldGdiObject);
10,Windows对任何企图删除库存GDI对象的行为都不予理会。可以利用SelectObject函数把库存GDI对象选入,从而删除当前GDI对象。
原因:
This function SelectObject may return a pointer to a temporary object. This temporary object is only valid during the processing of one Windows message.
如:
pDC->SelectObject(&newGdiObject);
...
pDC->SelectStockObject(StockCGdiObject);//如:BLACK_BRUSH
11,对于显示设备环境,在消息处理函数内部所进行GDI选择在函数退出后不再有效,因此在进入其他处理函数的时候,每次都必须重新设置设备环境。
Windows句柄是唯一能够持久存在的GDI标识。可以使用GDI对象指针调用GetSafeHandle函数可以获得它Windows句柄,再利用GDI对象类的FromHandle函数将句柄转化为对应的GDI对象。
如:
CFont *pOldFont=pDC->SelectObject(&newFont);
m_hOldFont=(HFONT)pOldFont->GetSafeHandle();//m_hOldFont为数据成员,保存先前字体对象句柄
...
pDC->SelectObject(CFont::FromHandle(m_hOldFont));//可在其他函数中,恢复m_hOldFont句柄对应的字体对象。
说明:
1)static CFont* PASCAL FromHandle( HFONT hFont );
//CFont::FromHandle Returns a pointer to a CFont object when given an HFONT handle to a Windows GDI font object. If a CFont object is not already attached to the handle, a temporary CFont object is created and attached.
2)CGdiObject::GetSafeHandle();
//Return A HANDLE to the attached Windows GDI object; otherwise NULL if no object is attached.
12,每一种Windows颜色都是 通过8位的红(R)、绿(G)、蓝(B)的值祝贺来表示的。
面向颜色的GDI函数可以接收32位的COLORREF参数。Windows的RGB宏可以将8位的红绿蓝值转化成COLORREF参数。
说明:
1)The COLORREF value is a 32-bit value used to specify an RGB color.
2)16种标准VGA纯色参见 P70页;256显示卡 多增加4种标准色参见 P71页。
13,字体是GDI对象,在使用上和其它的GDI对象完全一样。
CDC::GetTextMetrics
BOOL GetTextMetrics( LPTEXTMETRIC lpMetrics ) const;//测量字体高度等参数
14,GetDeviceCaps();
//Retrieves a wide range of device-specific information about the display device.
/**************************************/
第六章:模式对话框和Windows通用控件
1,对话框也是窗口,它不当可以接受消息,而且还可以被移动和关闭,甚至可以在它的客户区中进行绘图操作。
2,模式对话框在被关闭之前,用户无法在同一应用程序的其它地方进行工作。模式对话框更易于编程。
无模式对话框在它仍保留在屏幕的同时,用户还可以在应用程序的其它窗口进行工作。
3,对话框控件既可以通过CWnd指针来引用,也可以通过资源文件定义的索引值(和#define常量相关联)来引用。对话框控件本身就是一个窗口。控件通过向上级对话框发送消息来响应用户的动作。
利用ClassWizard可以帮助产生CDialog派生类,还可以使对话框类数据成员和对话框控件相联系。
4,在现存在项目中添加模式对话框步骤:
1)用对话框编辑器创建包含各种控件的对话框资源。对话框编辑器会对项目的资源文件更新,使之包含新的对话框资源,并且该项目的resource.h文件也会被更新,以便能够包含新的#define常量。
2)利用ClassWizard创建CDialog派生类,并将它和第一步所创建的资源相连接。
3)利用ClassWizard添加控件消息响应函数,并进行编辑。
4)在其它类中需要使用地方先构造该对话框类对象,再调用DoModal显示模式对话框。
说明:
1)在产生的对话框头文件中包含一个枚举类型常量IDD,用于设置该对话框资源ID。枚举常量IDD的使用减弱了CPP文件对资源ID的依赖。
2)《在其它类中显示模式对话框后,只有当模式对话框退出后,才继续执行该类显示模式对话框代码后的代码。》
5,在组合筐中Data属性页下,添加一条后按CTRL+RETURN来结束(或添加下条item)。(VC6中按?键可以获取相关帮助提示的)。组合框有Simple,Dropdown,Drop List三中风格,可以是Styles中选择。
列表筐中,用户只能用鼠标选择选择一个条目,而且在对话框编辑器中不能输入列表框的初始选择条目。
在静态文本控件中可以内置'&'符号。运行时候,&符号后面的字符下将有一个下划线。用户在按下ALT键后同时按下相应带下划线的字母,则就可以跳转到相应的控件上去。注意同一对话框中跳转字符不能重复。
6,当DoModal被调用后,实际在幕后引起一系列如下的动作:
CDialog::DoModal->CEx06Dialog::OnInitDialog->其它的初始化->CDialog::OnInitDialog->CWnd::UpdateData(FALSE)->CEx06Dialog::DoDataExchange
用户输入数据...
用户单击OK按钮
CEx06Dialog::OnOk->...其它的确认处理...->CDialog::OnOk->CWnd::UpDateData(TURE)->CEx06Dialog::DoDataExchange->CDialog::EndDialog(IDOK)
说明:
1)virtual BOOL OnInitDialog( );
//CDialog::OnInitDialog This member function is called in response to the WM_INITDIALOG message. This message is sent to the dialog box during the Create, CreateIndirect, or DoModal calls, which occur immediately before the dialog box is displayed.
//Override this member function if you need to perform special processing when the dialog box is initialized.
2)BOOL UpdateData( BOOL bSaveAndValidate = TRUE );
//CWnd::UpdateData :Call this member function to initialize data in a dialog box, or to retrieve and validate dialog data.
//bSaveAndValidate:Flag that indicates whether dialog box is being initialized (FALSE) or data is being retrieved (TRUE).
//By default UpdateData(TRUE) is called in the default CDialog::OnOK handler and UpdateData(FALSE) is called in the default CDialog::OnInitDialog.
3)virtual void DoDataExchange( CDataExchange* pDX );
//CWnd::DoDataExchange Called by the framework to exchange and validate dialog data.
//Never call this function (DoDataExchange) directly. It is called by the UpdateData member function.
//Call UpdateData to initialize a dialog box’s controls or retrieve data from a dialog box.
4)void EndDialog( int nResult );
//CDialog::EndDialog makes the dialog box invisible but does not destroy it.
//Call this member function to terminate a modal dialog box. This member function returns nResult as the return value of DoModal(IDOK表示接受对话框数据,IDCANCEL表示取消对话框数据). You must use the EndDialog function to complete processing whenever a modal dialog box is created.
//注意:If you implement the OK button in a modeless dialog box, you must override the OnOK member function and call DestroyWindow from within it.
5)当DoModal函数返回的时候,对话框窗口不再存在。我们可以在堆栈中创建模式对话框对象,这样就可以保证当程序控制转向到C++对话框对象所在的范围之外时,它及时的删除掉。
7,代码解释:
void CEx06aDialog::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CEx06aDialog)
DDX_Text(pDX, IDC_BIO, m_strBio);//
DDX_Radio(pDX, IDC_CAT, m_nCat);
...
DDV_MinMaxInt(pDX, m_nSsn, 0, 999999999);
//}}AFX_DATA_MAP
}
说明:
1)DoDataExchange、DDX_(交换)、DDV_(确认)函数都具有双向性。如果调用UpdateData(FALSE)(参数为FALSE),则这些数据就会将与控件相关联(值关联)的数据成员的值传递给对话框中的控件;相反,如果UpDateData(TURE)(参数是TURE),则这些函数会将数据从对话框控件中传递给与之相关联(值关联)的数据成员。
2)这里DDX_Text函数被重载,具体参阅MSDN。The DDX_Text function manages the transfer of int, UINT, long, DWORD, CString, float, or double data between an edit control in a dialog box, form view, or control view and a CString data member of the dialog box, form view, or control view object.
其它DDX_函数参阅MSDN。
8,Enter键触发OnOk函数调用:
当用户按下ENTER键后,OS会自动去查找输入焦点落在哪个按钮上,获得焦点的按钮四周将被点线矩形框包围。如果所有的按钮都没获得输入焦点,则OS会自动去寻找程序或资源所指定的默认按钮(默认按钮边框较粗)。如果对话框没有默认按钮,那么即使对话框中没有OK按钮,OnOk函数也会自动被调用。
由于OnOk函数是虚函数,可以重写OnOk函数,将其函数体置空,变可以使使ENTER键无效。
分离OK按钮:改写OK按钮ID,并除掉其默认按钮(Default Button)的设置。
9,p107至WIN32程序员:强烈建议看看,下为简要笔记。
对话框控件会向他们父对话框发送WM_COMMAND通告消息。大多数窗口程序处理函数都是通过一个嵌套的switch结构了处理这些通告消息的,而MFC则更直接了当地把这些控件通知消息和其它的Windows消息放在同一个层次上处理。
10,Esc键触发OnCancel函数的调用,导致控制从对话框中退出,如果是模式对话框则DoModal的还返回IDCANCEL值。其消除方法同ENTER键。
11,事例代码涉及的一些主要函数及补充说明:
1)CWnd* GetDlgItem( int nID ) const;//可用它来获取对话框上控件指针(返回的时候,强制转换成所需要的控件指针),注意临时性。
void CWnd::GetDlgItem( int nID, HWND* phWnd ) const;
//Retrieves a pointer to the specified control or child window in a dialog box or other window. The pointer returned is usually cast to the type of control identified by nID.
说明:如果需要包CWnd指针转化成一个控件ID,则可以使用MFC中CWnd类GetDlgCtrlID成员函数。
2)GetScrollPos Retrieves the current position of a scroll box.
SetScrollPos Sets the current position of a scroll box.
GetScrollRange Retrieves the current minimum and maximum scroll-bar positions for the given scroll bar.
SetScrollRange Sets minimum and maximum position values for the given scroll bar.
3)afx_msg void OnHScroll( UINT nSBCode, UINT nPos, CScrollBar* pScrollBar );//CWnd::OnHScroll
afx_msg void OnVScroll( UINT nSBCode, UINT nPos, CScrollBar* pScrollBar );//CWnd::OnVScroll
3)所有的水平滚动条都绑在WM_HSCROLL消息控制函数,所有的竖直滚动条则都绑在一个WM_VSCROLL消息控制函数上。(一般控件都有自己的独立的消息控制函数,但[滚动条控件]有点不同,所以对同一个对话框多个滚动条消息处理的时候,一般都将它们的滚动范围设置一致,方便编程)
/////////////////////////
/////////////////////////
///P109-125页内容笔记待续
12,每个控件在显示之前会向其父对话框发送WM_CTLCOLOR消息,对话框本身也会发送该消息。
《对话框和对话框控件都是窗口,也可以在这些窗口中进行绘图操作。(添加OnPain函数,调用Invalidate/UpdateWindow让窗口重绘)》
13,在运行时加入对话框控件:
一般步骤:
1)在对话框类中添加一个内嵌的控件窗口数据成员。一个内嵌控件C++对象将与相应的对话框对象一起被构造和删除。
2)从View菜单中选择Resource Symbols,为新的控件添加一个ID常量。(也可以在Resource.h中用定义)
3)用ClassWizard映射WM_INITDIALOG消息覆盖(Override)基类 CDialog::OnInitDialog函数。在该函数里调用控件窗口的Create成员函数,并显示新的控件(可以用ShowWindow函数)。Windows回在删除对话框窗口的同时也删除该控件窗口。
4)在派生对话框类里,为新控件手工加入必要的通知消息控制函数。(三步:消息控制函数原型说明,消息映射,消息处理函数)
14,Win32编程经验:父窗口和控件之间是通过Windows消息进行通信的(如MFC中象CListBox::InserString函数),但适用于所有窗口类型的控件类成员函数并不发送消息,而是调用Win32函数。
15,Windows通用控件:Win95引进的象[进度指示器],[滑杆条],[微调按钮控件],[列表控件]和[树状控件]。这些控件的代码在Windows的COMCTL32.DLL文件中,其中包括了每个控件的窗口过程函数,及每个控件的注册窗口类的代码,这些注册代码在DLL加载时候被调用。当应用程序初始化对话框时,将使用对话框资源中符号化的类名,来连接到DLL里的窗口过程函数上。这样应用程序就有了控件的窗口,但代码实际上在DLL里。
除了ActiveX控件,大多数控件都是用这种方法实现的。
16,WM_NOTIFY
idCtrl = (int) wParam; //控件ID
pnmh = (LPNMHDR) lParam; //指向NMHDR结构指针,该结构由控件管理。
说明:
1)Sent by a common control to its parent window when an event has occurred in the control or the control requires some kind of information.
2)Not all controls will send WM_NOTIFY messages. In particular, the standard Windows controls (edit controls, combo boxes, list boxes, buttons, scroll bars, and static controls) do not send WM_NOTIFY messages.
/////////////////////////
/////////////////////////
///P115-125页内容笔记待续
/*****************************************/
第七章:无模式对话框 和 Windows通用对话框类
1,[无模式对话框]在它处于激活状态下还允许用户在(同一个应用程序中)其它地方工作。
[通用对话框]则是C++和一组Windows的实用对话框之间的程序设计借口,包括File Open,Page Setup,Color等等,它们都是通过COMDLG32.DLL来实现的。
2,两种发送Windows消息:
CWnd::SendMessage//立刻导致对窗口控制函数的调用
CWnd::PostMessage//将消息放进Windows消息队列。对消息的处理可能被滞后。
具体:
1)LRESULT SendMessage( UINT message, WPARAM wParam = 0, LPARAM lParam = 0 );
//Sends the specified message to this window. The SendMessage member function calls the window procedure directly and does not return until that window procedure has processed the message. This is in contrast to the PostMessage member function, which places the message into the window’s message queue and returns immediately.
2)BOOL PostMessage( UINT message, WPARAM wParam = 0, LPARAM lParam = 0 );
//Places a message in the window’s message queue and then returns without waiting for the corresponding window to process the message. Messages in a message queue are retrieved by calls to the GetMessage or PeekMessage Windows function.
3,对话框实际上应该属于应用程序的主框架窗口,而不属于视图。(对话框默认弹出特性)
(注:还未领悟,先留着。)
4,对话框窗口的创建和取消完全取决与用户的操作,而对话框对象则将直到应用程序被终止时才会被删除。
(除了主框架窗口之外,对于几乎所有的窗口类型,DestroyWindow函数都不会将C++对象删除掉。所以要注意手动添加删除对话框对象代码)
5,Windows 常量WM_USER是用户自定义消息中可以利用的第一个消息ID。
#define WM_USER 0x0400
//The WM_USER constant is used by applications to help define private messages, usually of the form WM_USER+X, where X is an integer value.
说明:
1)CWnd::PostMessage//发送消息。利用wParam , LPARAM可以向响应消息的处理函数传送附加数据信息。
BOOL PostMessage( UINT message, WPARAM wParam = 0, LPARAM lParam = 0 );
2)在WIN32中,用wParam 和LPARAM参数来传递消息数据是最常用的手段(如:将鼠标的X,Y坐标压缩进lParam)。而在MFC库中,消息数据可以更多样的类型来传递(如:可以CPoint对象来传递鼠标信息)。
对于用户自定义消息,只能使用wParam 和LPARAM参数来传递消息附加数据信息。
3)案例说明:
在对话框类中:
#define WM_GOODBYE WM_USER + 5//定义自定义消息
m_pView->PostMessage(WM_GOODBYE, IDOK);//向View类发送WM_GOODBYE消息,附加消息IDOK存放在wParam 中。m_pView指向当前View类对象。
在View 类对象中
afx_msg LRESULT OnGoodbye(WPARAM wParam, LPARAM lParam);
ON_MESSAGE(WM_GOODBYE, OnGoodbye)
LRESULT CEx07aView::OnGoodbye(WPARAM wParam, LPARAM lParam)
{
return 0L;
}
4)技巧:在对话框类中重载构造函数,参数为CView*指针。再在对话框类中定义一个CView*指针数据成员。这样,如果在View类中通过传入this指针来构造对话框对象的时候,对话框类中CView*指针数据成员可以在带参数为CView*指针重载构造函数里方便获取构造它的View类指针。
6,ClassWizard并不支持用户自定义消息的响应,所以当使用用户自定义消息编程的时候,必须自己编写自定义消息的处理代码。(三步,首先是消息响应函数原型声明,其次消息映射,最后是编写消息响应函数代码。这里要注意:用户自定义消息的消息映射一定要加在BEGIN_MESSAGE_MAP(..)~~END_MESSAGE_MAP()之间,//{{AFX_MSG_MAP(CEx07aView)~~ //}}AFX_MSG_MAP注释宏对之外)
7,对于无模式对话框一定要注意不要调用CDialog::OnOk或者CDialog::OnCancel函数,既在无模式对话框类中必须重载这些虚函数;否则当使用ESC键,回车键或者用鼠标单击OK|CANCEL按钮的时候,会激发对应基类函数的调用,进而导致调用Windows 的EndDialog函数,EndDialog函数只适合于模式对话框。对于无模式对话框,必须调用DestroyWindow函数。
如果需要的话,还可调用Updatedata函数来将数据从对话框控件中传到类数据成员中。
8,Windows通用对话框:
共同特点:都从用户处获得消息,但并不对信息做处理。如:文件对话框只为程序提供路径名,字体对话框只是填充一个描叙字体的结构,并不创建字体。
所有的通用对话框类都从公有基类CCommonDialog派生而来。
COMDLG32中类列表如下:
CColorDialog 允许用户选择或创建颜色
CFileDialog 允许用户打开或者保存一个文件
CFindReplaceDialog 允许用户将一个字符串换成另一个字符串
CPageSetupDialog 允许用户输入页面参数
CFontDialog 允许用户从列出的可用字体中选择一种字体
CPrintDialog 允许用户设置打印机并打印文档
9,注意:在Win32中,不能在标准文件对话框内部动态创建控件。(其它标准对话框中也应该如此吧)
10,嵌套对话框(这些内容熔入EX07B事例中讲解了,不打算重复,强烈建议看看和跟着做做,页码:P135-141。下面只对其中重要的函数做些说明笔记。)
利用MFC,从通用
1)CFileDialog::m_ofn
//m_ofn is a structure of type OPENFILENAME. Use this structure to initialize the appearance of a File Open or File Save As dialog box after it is constructed but before it is displayed with the DoModal member function.
2)CFileDialog::CFileDialog
CFileDialog( BOOL bOpenFileDialog, LPCTSTR lpszDefExt = NULL, LPCTSTR lpszFileName = NULL, DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, LPCTSTR lpszFilter = NULL, CWnd* pParentWnd = NULL );
//bOpenFileDialog
Set to TRUE to construct a File Open dialog box or FALSE to construct a File Save As dialog box.
3)CFileDialog::DoModal
//Call this function to display the Windows common file dialog box and allow the user to browse files and directories and enter a filename.
//If you want to initialize the various file dialog-box options by setting members of the m_ofn structure, you should do this before calling DoModal, but after the dialog object is constructed.
//When the user clicks the dialog box’s OK or Cancel buttons, or selects the Close option from the dialog box’s control menu, control is returned to your application. You can then call other member functions to retrieve the settings or information the user inputs into the dialog box.
4)CFileDialog::GetPathName
//Call this function to retrieve the full path of the file entered in the dialog box. The path of the filename includes the file’s title plus the entire directory path.
5)事例中注意设置自己创建的子对话框上的组筐控件的ID为stc32。这样才保证文件通用对话框嵌入的位置在组筐所在的位置上,否则默认为自己创建的子对话框的同宽度。(stc32应该是与文件通用对话框相关联的,具体是如何关联的哦?)
6)事例中可见到这样的代码(GetParent()->GetDlgItem(IDOK)->SetWindowText("Delete");)来获取文件通用对话框上的控件指针,这里要理解为什么要用GetParent()函数来获得父窗口指针(因为事例中自己所创建的对话框被设置成Child Style。)(Child style,None border,Group box ID=stc32这些设置都是必不可少的,自己可以试着改变这些设置,看看效果印象也就深了)
7)事例中CSpecialFileDialog::OnDelete() 函数中代码(GetParent()->GetDlgItem(0x480)->GetWindowText(m_strFileName);)通过获取文件通用对话框上文件名对应的编辑框的指针调用CWnd::GetWindowText函数来获取编辑框中的文本保存在m_strFileName数据成员中。其中0x480应该是文件通用对话框上文件名对应的编辑框的ID。
8)事例中CSpecialFileDialog::OnDelete() 函数中代码(GetParent()->SendMessage(WM_COMMAND,IDCANCEL);)向文件通用对话框发送IDCANCEL消息,该操作引起OnCancel函数的调用终止当前模式对话框同时使得CFileDialog::DoModal函数返回IDCANCEL值。
MSDN:
CDialog::OnCancel (The default simply terminates a modal dialog box by calling EndDialog and causing DoModal to return IDCANCEL.)
9)SDK函数:FindFirstFile,DeleteFile
//FindFirstFile Searches a directory for a file whose name matches the specified file name on the destination site identified by this object. It examines subdirectory names as well as file names.
//DeleteFile Deletes the given file from the destination site.
10)CFile::Remove
A static function ,deletes the file specified by the path. It will not remove a directory. (注意:用这函数删除的文件是不经过回收站的哦。)
11,在对话框标题栏添加图标:在对话框类OnInitDialog函数中添加如下代码。
HICON hIcon =AfxGetApp()->LoadIcon(ID_MYICON);//ID_MYICON是用资源编辑器创建的图标ID。
this->SetIcon(hIcon,TRUE);
this->SetIcon(hIcon,FALSE);注:这里带上this指针目的是为了强调必须使用目的对话框类对象指针调用SetIcon设置图标。
比如在书EX07B事例中,由于CSpecialFileDialog类设置为子窗口类,而且所关联的资源窗口没有Tittle Bar,要在父窗口文件通用对话框Tittle Bar上添加图标,必须获取父窗口文件通用对话框对象指针来调用SetIcon设置图标。
如在书EX07B事例中:在CSpecialFileDialog::OnInitDialog函数中添加如下代码,可设置文件通用对话框标题图标:
HICON hIcon =AfxGetApp()->LoadIcon(ID_MYICON);
GetParent()->SetIcon(hIcon,TRUE);
GetParent()->SetIcon(hIcon,FALSE);
/////////////
/////////////
/************************************/
第八章:使用ActiveX控件
1,ActiveX控件是一个直接插入到C++程序中的软件模块,以前常称OLE控件(OCX),是基于MS-COM技术。
2,ActiveX控件与普通Windows控件比较:
相同点:ActiveX控件也可看成是一个子窗口(可以看成这样的)。
如果想在对话框上加入ActiveX控件,则只要在对话框编辑器中,把ActiveX控件放在适当的位置上,并在资源模板中标识该控件。如果要在运行的过程中建立ActiveX控件,则可以调用响应控件类的Create成员函数,而且通常在父窗口的WM_CREATE消息控制函数中调用。
不同点:属性和方法。
ActiveX控件不像普通控件那样发送以WM_打头的通知消息给它的包容器窗口,而是激发事件。事件实际上是由控件调用包容器函数。像普通的控件通知消息一样,事件并没有返回值传给ActiveX控件。事件如lick,KeyDown。但对于客户来说时间与控件的通知消息是一样的。
3,在MFC库中,ActiveX控件就像子窗口一样,但在控件窗口和包容器窗口之间有一层重要代码。实际上,ActiveX控件可能没有窗口。当调用Create函数时,并不是直接建立控件窗口,而是把控件代码载入进来,并激发一个“实地激活”(in-place activation)命令。然后ActiveX控件再建立它自己的窗口,通过MFC的CWnd类指针我们可以访问该窗口。不过客户程序最好不要使用ActiveX控件的hWnd句柄。
4,通常ActiveX控件会保存在扩展名为OCX的动态连接库中。包容器程序回根据Windows注册表利用COM技术在需要的时候装入动态连接库。
说明:
1)暂时可以这样认为,如果使用了ActiveX控件,那么在运行时候要装入该ActiveX控件代码。显然在发布含有ActiveX控件的程序时候,必须要包含相应的OCX文件,而且还得提供一个合适的安装程序。
5,安装ActiveX控件:
1)把找到的ActiveX控件动态连接库拷到硬盘上。
2)在WINDOWS注册表中登记注册。(可使用Regsvr32命令行命令)
3)在使用该控件的项目中安装该控件。(选择Project菜单,再选择Add To Project,再选择Components And Controls,再选择Registered ActiveX Controls,这时列表框列出系统已经注册所有的ActiveX控件,选择需要的控件INSERT即可。)
6,ActiveX控件包容器编程:
1)不管ActiveX控件是作为对话框控件,还是做为“子窗口”,MFC和ClassWizard都支持。
2)ActiveX控件编写者设计了ActiveX控件属性供使用者在设计时访问。所有的ActiveX控件属性(包括设计时属性),在运行时都是可以访问的,不过有些属性可能被设计成只读的。
3)当在项目中插入ActiveX控件时,ClassWizard就会产生相应的CWnd的派生类C++类,来满足对空间的方法和属性进行访问要求。控件的属性和方法都有相应的成员函数,同时生成的类还有一个构造函数可用以动态创建ActiveX控件的事例。
4)当在项目中插入ActiveX控件ClassWizard生成的CWnd的派生类C++类中,可以看到其成员函数的代码中都有对InvokeHelper函数的调用,InvokeHelper函数的第一个参数都和对应的属性或方法在ActiveX控件中的分发(dispatch)ID(标识ActiveX控件的方法或属性的)相对应。通过查看ActiveX控件hlp文件可以发现,ActiveX控件的方法在生存的C++类中都有同名的成员函数与之对应,ActiveX控件的属性都有一组Get和Set函数对其操作,其中ActiveX控件的方法和属性操作与生成的C++类成员函数相关联都是通过InvokeHelper函数的调用来完成的,InvokeHelper函数的第一个参数是由Component Gallery(控件提供者)提供的。因为经过这样的处理,所以我们如果要调用ActiveX控件的方法或对其属性进行取和设置操作,只需调用生成的C++类对应的成员函数便可。
下面对InvokeHelper单独说明:
CWnd::InvokeHelper
void InvokeHelper( DISPID dwDispID, WORD wFlags, VARTYPE vtRet, void* pvRet, const BYTE* pbParamInfo, ... );
说明:
Call this member function to invoke the OLE control method or property specified by dwDispID, in the context specified by wFlags.
其中参数:
dwDispID:
//Identifies the method or property to be invoked. This value is usually supplied by Component Gallery.
wFlags:可以为下面些值,指明调用InvokeHelper的目的。
//[ DISPATCH_METHOD ] The member is invoked as a method. If a property has the same name, both this and the DISPATCH_PROPERTYGET flag may be set.
[ DISPATCH_PROPERTYGET ] The member is retrieved as a property or data member.
[ DISPATCH_PROPERTYPUT ] The member is changed as a property or data member.
[ DISPATCH_PROPERTYPUTREF ] The member is changed by a reference assignment, rather than a value assignment. This flag is valid only when the property accepts a reference to an object.
vtRet:
//Specifies the type of the return value.
VT_EMPTY void
VT_I2 short
VT_I4 long
VT_R4 float
VT_R8 double
VT_CY CY
VT_DATE DATE
VT_BSTR BSTR
VT_DISPATCH LPDISPATCH
VT_ERROR SCODE
VT_BOOL BOOL
VT_VARIANT VARIANT
VT_UNKNOWN LPUNKNOWN
pvRet:
//Address of the variable that will that will receive the property value or return value. It must match the type specified by vtRet.
pbParamInfo:一般都设置为NULL
//Pointer to a null-terminated string of bytes specifying the types of the parameters following pbParamInfo.
specifies the types of the parameters passed to the method or property.
...:
//Variable List of parameters, of types specified in pbParamInfo.
5)AppWizard对ActiveX控件的支持是通过在生成的应用程序类的成员函数InitInstance中插入(AfxEnableControlContainer();),同时在响应项目文件的StdAfx.h文件中插入(#include<afxdisp.h>)(原因可参考书P38一些说明)。
如果项目中不包含这两行,而又要加入ActiveX控件,则只要手工加入上面两行代码即可。
6)可以对话框编辑器来生成对话框的模板中加入一个或多个ActiveX控件,这样我们可以在对话框模板生成的类中添加数据成员或事件控制函数来获取ActiveX控件的属性或对其控制。
注意:(详细见书P159页的[致WIN32程序员])
实际上,资源模板并不是在对话框编辑器中所看的那样。函数CDialog::DoModal在把对话框模板交给WINDOWS内部的对话框过程之前,要先对模板进行预处理,即:它先会去掉所有的ActiveX控件,有剩下的控件建立对话框窗口,然后再装入ActiveX控件,激活它们并在正确的位置上创建它的窗口。
当模式对话框运行时候,MFC处理所有送给对话框消息时,是不管是有普通控件发送的,还是ActiveX控件发送的。故ActiveX控件虽然不是对话框模板一部分,但用户仍然可以用TAB键在所有的控件间切换。
7)调用UpdateData(FALSE)将会从所有的对话框控件中读取所有属性值。如果只需要得到ActiveX控件属性的话,可以调用ActiveX控件生成的C++类中Get函数(同样设置调用Set函数),这样就提高了效率。
8)事例部分代码对比说明:
代码一:
CDataExchange dx(this,TRUE);
DDX_Text(&dx,IDC_DAY,m_sDay);
DDX_Text(&dx,IDC_MONTH,m_sMonth);
DDX_Text(&dx,IDC_YEAR,m_sYear);
说明一:
CDataExchange类构造函数:(注,在MFC|SRC|AFXWIN.H中可以看到其构造函数声明,在MFC|SRC|WINCORE.CPP文件中可以看到其构造函数的定义。)
原型:CDataExchange::CDataExchange(CWnd* pDlgWnd, BOOL bSaveAndValidate);
定义:
CDataExchange::CDataExchange(CWnd* pDlgWnd, BOOL bSaveAndValidate)
{
ASSERT_VALID(pDlgWnd);
m_bSaveAndValidate = bSaveAndValidate;
m_pDlgWnd = pDlgWnd;
m_hWndLastControl = NULL;
}
//其中m_pDlgWnd和m_bSaveAndValidate是CDataExchange数据成员,可以通过这中方式给它们赋值。
m_pDlgWnd:The dialog box or window where the data exchange takes place.
m_bSaveAndValidate Flag for the direction of DDX and DDV. 详见说明二。
说明二:
//CDataExchange does not have a base class.
//The CDataExchange class supports the dialog data exchange (DDX) and dialog data validation (DDV) routines used by the Microsoft Foundation classes. Use this class if you are writing data exchange routines for custom data types or controls, or if you are writing your own data validation routines.
//A CDataExchange object provides the context information needed for DDX and DDV to take place. The flag m_bSaveAndValidate is FALSE when DDX is used to fill the initial values of dialog controls from data members. The flag m_bSaveAndValidate is TRUE when DDX is used to set the current values of dialog controls into data members and when DDV is used to validate the data values. If the DDV validation fails, the DDV procedure will display a message box explaining the input error. The DDV procedure will then call Fail to reset the focus to the offending control and throw an exception to stop the validation process.
代码二:(是为了比较代码一做些说明的)
void CActiveXDialog::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);//调用基类的DoDataExchange
//{{AFX_DATA_MAP(CActiveXDialog)
DDX_Control(pDX, IDC_CALENDAR1, m_calendar);
DDX_Text(pDX, IDC_DAY, m_sDay);
DDX_Text(pDX, IDC_MONTH, m_sMonth);
DDX_Text(pDX, IDC_YEAR, m_sYear);
//}}AFX_DATA_MAP
}
说明一:
//DoDataExchange Called by the framework to exchange and validate dialog data.
//DoDataExchange Never call this function directly. It is called by the UpdateData member function. Call UpdateData to initialize a dialog box’s controls or retrieve data from a dialog box.
说明二:
//在MFC|SRC|WINCORE.CPP文件中可以看到UpdateData函数的定义
BOOL CWnd::UpdateData(BOOL bSaveAndValidate)
{ ...
CDataExchange dx(this, bSaveAndValidate);//创建了一个CDataExchange对象,与当前窗口相关联
...
DoDataExchange(&dx); //注意:DoDataExchange是个虚函数。子类中如果有重写了,则调用子类的。
...
}
说明三:
//在MFC|Include|AFXWIN2.INL文件中可有看到CWnd::DoDataExchange的如下定义(内联):
// CWnd dialog data support
_AFXWIN_INLINE void CWnd::DoDataExchange(CDataExchange*)
{ } // default does nothing
由此可见代码二中CDialog::DoDataExchange(pDX)调用好象是个‘摆设’,不起做任何事情。框架设置DoDataExchange函数目的是在我们子窗口类(这里是对话筐)中重写它,添加代码完成子窗口类(这里是对话筐)中数据成员与对话筐上控件的交互。
说明四:
如果结合UpdateData和DoDataExchange两函数整体来看,应该体会到这里代码一与代码二实质上是一会事情。代码二只是借助了框架兜了些圈子。
说明四:
摘录MSDN中Dialog Data Exchange一些E文段落对上讨论做个总结:
Dialog Data Exchange
If you use the DDX mechanism, you set the initial values of the dialog object’s member variables, typically in your OnInitDialog handler or the dialog constructor. Immediately before the dialog is displayed, the framework’s DDX mechanism transfers the values of the member variables to the controls in the dialog box, where they appear when the dialog box itself appears in response to DoModal or Create. The default implementation of OnInitDialog in CDialog calls the UpdateData member function of class CWnd to initialize the controls in the dialog box.
The same mechanism transfers values from the controls to the member variables when the user clicks the OK button (or whenever you call the UpdateData member function with the argument TRUE). The dialog data validation mechanism validates any data items for which you specified validation rules.
UpdateData works in both directions, as specified by the BOOL parameter passed to it. To carry out the exchange, UpdateData sets up a CDataExchange object and calls your dialog class’s override of CDialog’s DoDataExchange member function. DoDataExchange takes an argument of type CDataExchange. The CDataExchange object passed to UpdateData represents the context of the exchange, defining such information as the direction of the exchange.
When you (or ClassWizard) override DoDataExchange, you specify a call to one DDX function per data member (control). Each DDX function knows how to exchange data in both directions based on the context supplied by the CDataExchange argument passed to your DoDataExchange by UpdateData.
MFC provides many DDX functions for different kinds of exchange.
If the user cancels a modal dialog box, the OnCancel member function terminates the dialog box and DoModal returns the value IDCANCEL. In that case, no data is exchanged between the dialog box and the dialog object.
9)当输入焦点在某ActiveX控件上时,按下F1键引起OnHelpInfo函数调用,可在OnHelpInfo函数中设置帮助信息。
说明:
ClassWizard不能修改生成的ActiveX控件类,因而必须手工加入消息映射代码。事例代码如下:
//在ActiveX控件类头文件中加入函数原型并声明消息映射表:
protected:
afx_msg BOOL OnHelpInfo(HELPINFO* pHelpInfo);
DECLARE_MESSAGE_MAP()//在ActiveX控件类代码文件中添加消息映射及OnHelpInfo函数定义:
BEGIN_MESSAGE_MAP(CCalendar,CWnd)
ON_WM_HELPINFO()
END_MESSAGE_MAP()
/**
BOOL CCalendar::OnHelpInfo(HELPINFO *pHelpInfo)
{
::WinHelp(GetSafeHwnd(),"C:\WINDOWS\system32\MSCAL.hlp",
HELP_FINDER,0);
return FALSE;
}
/////////////////
//////////////
///////////
7,在运行时创建ActiveX控件:
1)在项目中插入ActiveX控件。ClassWizard会生存相应的ActiveX控件类的文件。
2)在使用ActiveX控件的对话框或窗口类中添加ActiveX控件类数据成员。
3)重写CDialog::OnInitDialog(或其它窗口中响应WM_CREAT消息),在新的函数中调用ActiveX控件类Create函数。
4)在父窗口类中,手工添加必要的与新控件有关的事件消息处理函数及原型,和相应的消息映射。
/////////
8,更多的ActiveX控件编程参见P160-167页(ActiveX控件在HTML文件中使用 和 在运行时创建ActiveX控件)。