1. 在单文档中view挡在MainFrame的前面。此时如果编写针对MainFrame的mouseClick
事件,将不会有反应。因为MFC视类窗口是覆盖在框架窗口上的,因此框架窗口不能感
到鼠标消息.
2. MFC的消息映射机制:
在每个能接收和处理消息的类中,定义一个消息和消息函数对照表,即消息映射表.在消息映射表中,消息与对应的消息处理函数指针成对出现.某个类能处理的所有消息及其对应的消息处理函数的地址都列在这个类所对应的静态表中.当有消息需要处理时,程序只要搜索该消息静态表,查看表中是否含有该消息,就可知道该类能否处理此消息.如果能处理该消息,则同样依照静态表很容易找到并调用对应的消息处理函数.
MFC消息映射机制是针对能接受消息和处理消息的类来定义对应的消息映射表,而不是由父类来定义所有消息对应的虚函数,由子类来覆盖其函数实现,因为这样做会使程序背着一个很大的虚拟函数表的包袱运行,对内存是一种浪费.
MFC工程中一个消息映射在三处添加代码:
(1): CDrawView视类的头文件.h
//{{AFX_MSG(CDrawView)
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
两个AFX_MSG注释宏(因为加了注释符)之间,afx_msg是限定符(也是宏),表明函数是一个消息响应函数的声明,如果是用户自定义的消息函数响应声明则在注释宏下, DECLARE_MESSAGE_MAP之上加写代码
(2): CDrawView的cpp(源文件)的BEGIN_MESSAGE_MAP和END_MESSAGE_MAP之间,定义了CDrawView类的消息映射表,其中ON_WM_LBUTTONDOWN映射宏就是将鼠标左键按下消息(WM_LBUTTONDOWN)与一个消息响应函数(OnLButtonDown)关联.
BEGIN_MESSAGE_MAP(CDrawView, CView)
//{{AFX_MSG_MAP(CDrawView)
ON_WM_LBUTTONDOWN()
//}}AFX_MSG_MAP
// Standard printing commands
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()
如果添加自定义的消息映射,使用ON_MESSAGE(用户定义消息,消息响应函数名)无”;”结尾
(3): 是CDrawView的cpp(源文件)中有函数实现。
void CDrawView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TOD Add your message handler code here and/or call default
m_ptOrigin=m_ptOld=point;
m_bDraw=TRUE;
CView::OnLButtonDown(nFlags, point);
}
通过分析MFC消息响应函数在程序中有三处属地省:函数原型,用来关联消息和消息响应函数的宏和函数实现.
3. 以下绘图程序,参考代码的注释可解决部分绘图问题
void CLesson3View::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
//作图
/*调用SDK函数获取设备上下文句柄
HDC hdc;
hdc = ::GetDC(m_hWnd);//这个m_hWnd是CWnd类中的保护成员, 保存窗口句柄,
//而CLesson3View类是从CWnd类继承来的,所以也有这个成员
MoveToEx(hdc, m_ptnOrigin.x, m_ptnOrigin.y, NULL);
LineTo(hdc, point.x, point.y);
::ReleaseDC(m_hWnd, hdc); // 在使用完设备上下文句柄后一定注意释放*/
/*使用CDC(MFC)关于作图对HDC一个封装*/
// CDC *pDc;
// pDc = GetDC();
// pDc->MoveTo(m_ptnOrigin);
// pDc->LineTo(point);
// ReleaseDC(pDc);
/*使用客户区绘图类,这个是比较常用的*/
//CClientDC dc(this);//CClientDC的构造函数,使用当前窗口句柄值做为参数
//CClientDC dc(GetParent());//得到关于父类窗口一个设备上下文
// dc.MoveTo(m_ptnOrigin);
// dc.LineTo(point);
// CClientDC类在构造时调用GetDC,然后在释放时又调用ReleaseDC所以不用手动释放
//利用MFC的CWindowDC绘图
/好处是可以访问整个窗口区域,包括框架窗口客户区和非客户区,桌面等,
// CWindowDC dc(this);
// CWindowDC dc(GetParent());
// dc.MoveTo(m_ptnOrigin);
// dc.LineTo(point);
// CWindowDC dc(GetDesktopWindow());//这个可以画到桌面上其它地方
// dc.MoveTo(m_ptnOrigin);
// dc.LineTo(point);
//以上所画的线条颜色都是黑色的,因为在设备描述表中使用默认的画笔(黑色),
//要改变线条颜色则需要自己生成一个新的画笔对象,
//将它选到设备描述表中,再画就使用新画笔来绘图
// CPen m_pen(PS_DASH, 2, RGB(255, 0, 0));//生成新的画笔
// CClientDC dc(this);
// CPen *pOldPen = dc.SelectObject(&m_pen);//选择进设备描述表中
// dc.MoveTo(m_ptnOrigin);
// dc.LineTo(point);
// dc.SelectObject(pOldPen);//在使用完新的画笔后,要将原来的画笔重新选择时设备描述表
//使用画刷来填充矩形
// CBrush m_brush(RGB(120, 0, 23));
// CClientDC dc(this);
// dc.FillRect(CRect(m_ptnOrigin, point), &m_brush);
//使用位图画刷来填充矩形
//创建一个位图对象
// CBitmap m_bitmap;
// m_bitmap.LoadBitmap(IDB_MyBitmap);
// CBrush m_Brush(&m_bitmap);
// CClientDC dc(this);
// dc.FillRect(CRect(m_ptnOrigin, point), &m_Brush);
//透明画刷
//首先使用Win32的API函数GetStockObject来获取一个NULL_BRUSH画刷
CClientDC dc(this);
CBrush *pBrush = CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
//是静态成员函数,从句柄获取对象的指针
CBrush *pOldBrush = dc.SelectObject(pBrush);
dc.Rectangle(CRect(m_ptnOrigin, point));
dc.SelectObject(pOldBrush);
bIsMouseDown = FALSE;
CView::OnLButtonUp(nFlags, point);
}
类的静态成员函数可以由类名直接调用,也可以由对象调用。可以认为静态成员函数并不属于某个对象,它属于类本身。程序运行伊始,即使没有实例化类的对象,静态成员函数和静态成员变量已然有其内存空间。静态成员函数不能访问非静态成员变量!静态成员变量必须在类的外部初始化。当然如果并不打算用到静态成员变量,此时你可以不初始它。
4. 理解代码区,数据区,堆,栈!
对于一个进程的内存空间而言,可以在逻辑上分成3个部份:代码区,静态数据区和动态数据区。动态数据区一般就是“堆栈”。“栈(stack)”和“堆(heap)”是两种不同的动态数据区,栈是一种线性结构,堆是一种链式结构。进程的每个线程都有私有的“栈”,所以每个线程虽然代码一样,但本地变量的数据都是互不干扰。一个堆栈可以通过“基地址”和“栈顶”地址来描述。全局变量和静态变量分配在静态数据区,本地变量分配在动态数据区,即堆栈中。程序通过堆栈的基地址和偏移量来访问本地变量