MFC消息映射机制剖析.docx
《MFC消息映射机制剖析.docx》由会员分享,可在线阅读,更多相关《MFC消息映射机制剖析.docx(11页珍藏版)》请在冰豆网上搜索。
MFC消息映射机制剖析
一,消息映射机制
1,消息响应函数:
(例:
在CDrawView类响应鼠标左键按下消息)
1)在头文件(DrawView.h)中声明消息响应函数原型。
//{{AFX_MSG(CDrawView) //注释宏
afx_msgvoidOnLButtonDown(UINTnFlags,CPointpoint);
//}}AFX_MSG //注释宏
说明:
在注释宏之间的声明在VC中灰色显示。
afx_msg宏表示声明的是一个消息响应函数。
2)在源文件(DrawView.cpp)中进行消息映射。
BEGIN_MESSAGE_MAP(CDrawView,CView)
//{{AFX_MSG_MAP(CDrawView)
ON_WM_LBUTTONDOWN()
//}}AFX_MSG_MAP
//Standardprintingcommands
ON_COMMAND(ID_FILE_PRINT,CView:
:
OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT,CView:
:
OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW,CView:
:
OnFilePrintPreview)
END_MESSAGE_MAP()
说明:
在宏BEGIN_MESSAGE_MAP()与END_MESSAGE_MAP()之间进行消息映射。
宏ON_WM_LBUTTONDOWN()把消息WM_LBUTTONDOWN与它的响应函数OnLButtonDown()相关联。
这样一旦有消息的产生,就会自动调用相关联的消息响应函数去处理。
宏ON_WM_LBUTTONDOWN()定义如下:
#defineON_WM_LBUTTONDOWN()\
{WM_LBUTTONDOWN,0,0,0,AfxSig_vwp,\
(AFX_PMSG)(AFX_PMSGW)(void(AFX_MSG_CALLCWnd:
:
*)(UINT,CPoint))&OnLButtonDown},
3)源文件中进行消息响应函数处理。
(DrawView.cpp中自动生成OnLButtonDown函数轮廓,如下)
voidCDrawView:
:
OnLButtonDown(UINTnFlags,CPointpoint)
{
//TODO:
Addyourmessagehandlercodehereand/orcalldefault
CView:
:
OnLButtonDown(nFlags,point);
}
说明:
可见当增加一个消息响应处理,在以上三处进行了修改。
可在消息响应函数里添加消息处理代码完成对消息的响应、处理。
2,消息响应的方式:
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()之间寻找),最终找到对应的消息处理函数。
当然,如果子类中没有对消息进行处理,则消息交由基类处理。
说明:
virtualLRESULTWindowProc(UINTmessage,WPARAMwParam,LPARAMlParam);
virtualBOOLOnWndMsg(UINTmessage,WPARAMwParam,LPARAMlParam,LRESULT*pResult);
二,有关绘图
1,使用SDK获取DC句柄:
HDChdc;
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类派生来的)
CClientDCdc(this);
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
说明:
TheCClientDCclassisderivedfromCDCandtakescareofcallingtheWindowsfunctionsGetDCatconstructiontimeandReleaseDCatdestructiontime.ThismeansthatthedevicecontextassociatedwithaCClientDCobjectistheclientareaofawindow.
4,利用CWindowDC对象。
(CWindowDC类从CDC类派生来的)
CWindowDCdc(this);//
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
说明:
TheCWindowDCclassisderivedfromCDC.ItcallstheWindowsfunctionsGetWindowDCatconstructiontimeandReleaseDCatdestructiontime.ThismeansthataCWindowDCobjectaccessestheentirescreenareaofaCWnd(bothclientandnonclientareas).
5,GetParent()得到父窗口指针;GetDesktopWindow()得到屏幕窗口指针。
6,利用画笔改变线条颜色和类型:
CPenpen(PS_DOT,1,RGB(0,255,0));//构造画笔对象
CClientDCdc(this);CPen*pOldPen=dc.SelectObject(&pen);//将画笔选入DC
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
dc.SelectObject(pOldPen);//恢复先前的画笔
7,使用画刷(通常利用画刷去填充矩形区域):
使用单色画刷
CBrushbrush(RGB(255,0,0));//构造画刷对象
CClientDCdc(this);
dc.FillRect(CRect(m_ptOrigin,point),&brush);//用指定的画刷去填充矩形区域
使用位图画刷
CBitmapbitmap;//构造位图对象(使用前需要初试化)
bitmap.LoadBitmap(IDB_BITMAP1);//初试化位图对象
CBrushbrush(&bitmap);//构造位图画刷
CClientDCdc(this);
dc.FillRect(CRect(m_ptOrigin,point),&brush);//用指定的位图画刷去填充矩形区域
使用透明画刷
CBrush*pBrush=CBrush:
:
FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));//获取透明画刷对象指针
CClientDCdc(this);
CBrush*pOldBrush=dc.SelectObject(pBrush);//将透明画刷选入DC
dc.Rectangle(CRect(m_ptOrigin,point));
dc.SelectObject(pOldBrush);//释放透明画刷
说明:
TheGetStockObjectfunctionretrievesahandletooneofthepredefinedstockpens,brushes,fonts,orpalettes.
HGDIOBJGetStockObject(
intfnObject //typeofstockobject
);
ReturnsapointertoaCBrushobjectwhengivenahandletoaWindowsHBRUSHobject.
staticCBrush*PASCALFromHandle(HBRUSHhBrush);//FromHandle是一个静态方法,故可用CBrush:
:
FromHandle()形式调用。
注意点:
1)静态方法不属于某一个具体对象,而属于类本身,在类加载的时候就已经为类静态方法分配了代码去,故可用CBrush:
:
FromHandle()形式调用。
2)静态方法中,不能引用非静态的数据成员和方法。
3)静态数据成员需要在类外单独做初始化,形式如:
变量类型类名:
:
变量名=初始值;
8,CDC:
:
SetROP2方法:
intSetROP2(intnDrawMode);
Setsthecurrentdrawingmode.
(5)文本编程
1,创建插入符:
voidCreateSolidCaret(intnWidth,intnHeight);//创建插入符
voidCreateCaret(CBitmap*pBitmap);//创建位图插入符
voidShowCaret();//显示插入符
voidHideCaret();//隐藏插入符
staticvoidPASCALSetCaretPos(POINTpoint);//移动插入符号
说明:
1)创建插入符要在窗口创建完成之后,CreateSolidCaret函数创建的插入符被初始化为隐藏,所以需要调用ShowCaret()将其显示。
2)使用CreateCaret函数创建位图插入符的时候,不能使用局部的位图对象关联位图资源。
(与资源相关联的C++对象,当它析构的时候会同时把与它相关联的资源销毁。
)
2,获取当前字体信息的度量:
CDC:
:
GetTextMetrics
BOOLGetTextMetrics(LPTEXTMETRIClpMetrics)const;
说明:
typedefstructtagTEXTMETRIC{ /*tm*/
int tmHeight;//字体高度。
Specifiestheheight(ascent+descent)ofcharacters.
int tmAscent;//基线以上的字体高度
int tmDescent;//基线以下的字体高度
int tmInternalLeading;
int tmExternalLeading;
int tmAveCharWidth;//字符平均宽度
int tmMaxCharWidth;
int tmWeight;
BYTEtmItalic;
BYTEtmUnderlined;
BYTEtmStruckOut;
BYTEtmFirstChar;
BYTEtmLastChar;
BYTEtmDefaultChar;
BYTEtmBreakChar;
BYTEtmPitchAndFamily;
BYTEtmCharSet;
int tmOverhang;
int tmDigitizedAspectX;
int tmDigitizedAspectY;
}TEXTMETRIC;
3,OnDraw函数:
virtualvoidOnDraw(CDC*pDC)
当窗口(从无到有或尺寸大小改变等)要求重绘的时候,会发送WM_PAIN消息,调用OnDraw函数进行重绘。
4,获取字符串的高度和宽度(区别字符串的长度):
CDC:
:
GetTextExtent
CSizeGetTextExtent(LPCTSTRlpszString,intnCount)const;
CSizeGetTextExtent(constCString&str)const;
说明:
TheCSizeclassissimilartotheWindowsSIZEstructure。
typedefstructtagSIZE{
intcx;//thex-extent
intcy;//they-extent
}SIZE;
5,路径层:
BOOLBeginPath();
//在这作图定义路径层剪切区域
BOOLEndPath();
BOOLSelectClipPath(intnMode);//调用这个函数来使当前路径层剪切区域与新剪切区域进行互操作。
//在这覆盖作图(包含前定义的路径层区域)定义新的剪切区域
说明:
1)SelectClipPathSelectsthecurrentpathasaclippingregionforthedevicecontext,combiningthenewregionwithanyexistingclippingregionbyusingthespecifiedmode.Thedevicecontextidentifiedmustcontainaclosedpath.
////
nMode:
RGN_AND,RGN_COPY,RGN_DIFF,RGN_OR,RGN_XOR
RGN_AND Thenewclippingregionincludestheintersection(overlappingareas)ofthecurrentclippingregionandthecurrentpath.
RGN_COPY Thenewclippingregionisthecurrentpath.
RGN_DIFF Thenewclippingregionincludestheareasofthecurrentclippingregion,andthoseofthecurrentpathareexcluded.
RGN_OR Thenewclippingregionincludestheunion(combinedareas)ofthecurrentclippingregionandthecurrentpath.
RGN_XOR Thenewclippingregionincludestheunionofthecurrentclippingregionandthecurrentpath,butwithouttheoverlappingareas.
2)应用:
当作图的时候,如果想要在整幅图形其中的某个部分和其它部分有所区别,我们可以把这部分图形放到路径层当中,然后指定调用指定互操作模式调用SelectClipPath(intnMode)函数来使路径层和覆盖在其上新绘图剪切区域进行互操作,达到特殊效果。
6,关于文本字符串一些函数:
COLORREFGetBkColor()const;//得到背景颜色
virtualCOLORREFSetBkColor(COLORREFcrColor);//设置背景颜色
BOOLSetTextBkColor(COLORREFcr);//设置文本背景颜色
virtualCOLORREFSetTextColor(COLORREFcrColor);//设置文本颜色
virtualBOOLTextOut(intx,inty,LPCTSTRlpszString,intnCount);//输出文本
BOOLTextOut(intx,inty,constCString&str);
CStringLeft(intnCount)const;//得到字符串左边nCount个字符
intGetLength()const;//得到字符串长度
7,字体CFont:
:
CFont
CFont();//构造函数
//ConstructsaCFontobject.TheresultingobjectmustbeinitializedwithCreateFont,CreateFontIndirect,CreatePointFont,orCreatePointFontIndirectbeforeitcanbeused.
选用字体事例代码组:
CClientDCdc(this);
CFontfont;//构造字体对象
font.CreatePointFont(300,"华文行楷",NULL);//初始化字体对象,与字体资源相关联
CFont*pOldFont=dc.SelectObject(&font);//将新字体选入DC
...
dc.SelectObject(pOldFont);//恢复原字体
说明:
1)构造字体对象时候,必须初始化。
(初始化是将字体对象与字体资源相关联)。
2)初始化对象时候,选用的字体也可以是系统字体,但不一定都有效,据测试选用。
8,在MFC中CEditView和cRichEditView类已经完成了初步的文字处理。
可以让应用程序的View类以CEditView和cRichEditView类为基类。
9,平滑变色
CDC:
:
TextOut()是一个字母一个字母的输出,达不到平滑效果。
CDC:
:
DrawText():
将文字的输出局限于一个矩形区域,超出矩形区域的文字都被截断。
利用这一特点,可每隔些时间增加矩形大小,从而可实现人眼中的平滑效果。
CWnd:
:
SetTimer():
设置定时器。
按设定的时间定时发送WM_TIMER消息。
说明:
UINTSetTimer(UINTnIDEvent,UINTnElapse,void(CALLBACKEXPORT*lpfnTimer)(HWND,UINT,UINT,DWORD));
//nIDEvent定时器标示,nElapse消息发送间隔时间,void(CALLBACKEXPORT*lpfnTimer)(HWND,UINT,UINT,DWORD)设置回调函数,如果设置则由设置的回调函数处理WM_TIMER消息,如果没有设置回调函数设为NULL,这发送的WM_TIMER消息压入消息队列,交由相关联的窗口处理(添加WM_TIMER消息处理函数OnTimer())。
afx_msgvoidOnTimer(UINTnIDEvent);
//响应WM_TIMER消息,nIDEvent为消息对应定时器标示(可以设置不同的定时器发送WM_TIMER消息)
问题:
1,在CCareView类中添加WM_CREATE消息响应函数OnCreate(),WM_CREATE消息是在什么时候被检查到而被响应的呢?
(猜测:
添加WM_CREATE消息后,消息被压入消息队列,然后经过消息循环进行分发到具体窗口,从而进行响应)
2,现有一文本文件内容已经读入串STR中,要求在视图客户区按原先文本文件中的格式输出。
问题是,利用CDC的TextOut()来在CView类派生类窗口中输出串时,忽略了串中的TAB、回车换行等格式,无论串有多长均在一行上输出。
这其中是CDC类成员函数TextOut()忽略串中格式的,还是CView类派生类窗口设置从中做怪呢?
怎么解决?
(6)菜单编程
1,弹出菜单(Pop-up)是不能用来作命令响应的。
2,MFC中菜单项消息如果利用ClassWizard来对菜单项消息分别在上述四个类中进行响应,则菜单消息传递顺序:
View类--Doc类--CMainFrame类--App类。
菜单消息一旦在其中一个类中响应则不再在其它类中查找响应函数。
具体:
当点击一个菜单项的时候,最先接受到菜单项消息的是CMainFrame框架类,CMainFrame框架类将会把菜单项消息交给它的子窗口View类,由View类首先进行恚蝗绻鸙iew类检测到没对该菜单项消息做响应,则View类把菜单项消息交由文档类Doc类进行处理;如果Doc类检测到Doc类中也没对该菜单项消息做响应,则Doc类又把该菜单项消息交还给View类,由View类再交还给CMainFrame类处理。
如果CMainFrame类查看到CMainFrame类中也没对该消息做响应,则最终交给App类进行处理。
3,消息的分类:
标准消息,命令消息,通告消息。
[标准消息]:
除WM_COMMAND之外,所有以WM_开头的消息。
[命令消息]:
来自菜单、加速键或工具栏按钮的消息。
这类消息都以WM_COMMAND呈现。
在MFC中,通过菜单项的标识(ID)来区分不同的命令消息;在SDK中,通过消息的wParam参数识别。
[通告消息]:
由控件产生的消息,例如,按钮的单击,列表框的选择等均产生此类消息,为的是向其父窗口(通常是对话框)通知事件的发生。
这类消息也是以WM_COMMAND形式呈现。
说明:
1)从CWnd派生的类,都可以接收到[标准消息]。
2)从CCmdTarget派生的类,都可以接收到[命令消息]和[通告消息]。
4,一个菜单拦可以有若干个子菜单,一个子菜单又可以有若干个菜单项等。
对菜单栏的子菜单由左至右建立从0开始的索引。
对特定子菜单的菜单项由上至下建立了从0开始的索引。
访问子菜单和菜单项均可以通过其索引或标识(如果有标识的话)进行。
相关重要函数:
CMenu*GetMenu();//CWnd:
:
GetMenu