1.窗口句柄映射表和消息映射表
2.视类窗口总是覆盖在框架类窗口之上,所有操作,包括鼠标单击,键盘按下,鼠标移动等操作都只能由视类窗口捕捉。框架类中收不到这些消息。
3.删除消息响应函数:右键该函数名—delete(最好不要手工删除,因为系统添加的代码可能忘了删除)
4.消息映射机制
(1)添加消息响应函数,系统添加的代码:
第一处:消息响应函数原型
//{{AFX_MSG(CTestView)(AFX_MSG 注释宏)
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
//}}AFX_MSG
(CTestView类的头文件中)
第二处:ON_WM_LBUTTONDOWN()消息映射宏—把消息和消息响应函数关联起来
EGIN_MESSAGE_MAP(CTestView, CView)
//{{AFX_MSG_MAP(CTestView)
ON_WM_LBUTTONDOWN()
//}}AFX_MSG_MAP
// Standard printing commands
……
END_MESSAGE_MAP()
(CTestView类的源文件中)
第三处:消息响应函数的定义
void CTestView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CView::OnLButtonDown(nFlags, point);
}
(2)MFC消息映射机制的具体实现方法是:在每个能接受和处理消息的类中,定义一个消息和消息函数静态对照表,即消息映射表。在消息映射表中,消息与对应的消息处理函数指针是成对出现的。某个类能处理的所有消息及其对应的消息处理函数的地址都列在这个类所对应的静态表中。当有消息需要处理时,程序只要搜索该消息静态表,查看表中是否含有该消息,就可知道该类是否能处理此消息。如果能处理该消息,则同样依照静态表能容易找到并调用对应的消息处理函数。
(3)MFC消息映射机制的实际实现过程:MFC在后台维护了一个窗口和C++对象指针的对照表。如CTestView类,与该类的对象相关的窗口的句柄和它的对象的指针关联。
消息—窗口句柄(收到该消息的窗口)—将对应的对象指针传递给窗口类的基类—基类(CWnd)调用WindowProc函数(虚函数)—调用OnWndMsg(消息映射由此函数完成)—判断窗口子类指针是否能响应—如能,则处理;不能,交由基类处理。
5.绘制线条
(1)在CTestView中定义私有变量CPoint m_ptOrigin;
(2)在CTestView构造函数中初始化 m_ptOrigin=0;
(3)添加WM_LBUTTONDOWN消息响应函数,记录m_ptOrigin=point;
(4)添加WM_LBUTTONUP消息响应函数,利用SDK全局函数实现划线功能。
HDC hdc;
hdc=::GetDC(m_hWnd);
MoveToEx(hdc,m_ptOrigin.x,m_ptOrigin.y,NULL);//移动到线条起点
LineTo(hdc,point.x,point.y);//画线
::ReleaseDC(m_hWnd,hdc);//释放设备
Todo:CWnd类有一个成员变量(m_hWnd)用于保存窗口句柄。CTestView派生与CWnd,所以也有这样一个成员变量保存了窗口句柄
::访问全局函数(调用SDK函数)
(5)利用MFC的CDC类实现画线功能(效果同(4))
MFC为我们提供了一个设备描述表的封装类CDC,该类封装了所有与绘图相关的操作。该类提供一个成员数据m_hDC,用于保存与CDC类相关的DC句柄。
CDC *pDC=GetDC();//GetDC()是CWnd类的成员函数,获得当前窗口的设备描述表的指针。
pDC->MoveTo(m_ptOrigin);//参数是一个点
pDC->LineTo(point);
ReleaseDC(pDC);//CWnd的成员函数
(6)利用MFC的CClientDC类实现画线功能(同上)
CClientDC类派生与CDC类。并在构造时调用GetDC函数,析构时调用ReleaseDC函数,只需创建该类的对象就能画线,不用显示调用这两个函数。
CClientDC dc(this);//构造CClientDC对象时需要一个CWnd类型的指针作为参数,此时this指针指向CTestView对象。
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
6.以上几种画线方法只能在视窗口的客户区画线,不能在菜单条,工具条中画线,因为他们都是与视类(CTestView)相关。视窗口的父窗口就是框架窗口。要创建与框架窗口相关的CClientDC类,可以调用GetParent()函数获得视窗口的父窗口的指针。
CClientDC dc(GetParent());这样就可以在工具栏上画线了。
视窗口全部是客户区。
框架窗口的菜单栏和标题栏是非客户区,菜单栏以下(如工具栏)是客户区。
7.利用MFC的CWindowDC类实现画线功能
CWindowDC类派生与CDC类。并且在构造时调用GetWindowDC函数获得相应的设备描述表对象,析构时调用ReleaseDC释放资源。使用CWindowDC类对象可以访问整个窗口区域,包括非客户区。
CWindowDC dc(GetParent());
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
8.在桌面窗口中画线
CWnd类的GetDesktopWindow成员函数可以获得Windows桌面窗口的句柄。
CWindowDC dc(GetDesktopWindow ());
9.绘制彩色线条
(1)利用MFC提供的类CPen来创建画笔对象(该类封装了与画笔相关的操作)
构造函数一:CPen(
int nPenStyle,//笔的线型(实线PS_SOLID 、点线PS_DOT、虚线PS_DASH)
int nWidth,//笔的宽度
COLORREF crColor//笔的颜色
);
COLORREF RGB(BYTE bRed,BYTE bGreen,BYTE bBlue);
(2)在程序中,构造了一个GDI对象后,该对象不会立即生效,必须选入设备描述表,它才会在以后的绘制操作中生效。利用SelectObject函数可以把GDI对象选入设备描述表,并且返回指向先前被选对象的指针。(为了绘制完成后,还原设备描述表),一般情况下,完成绘图之后,都要利用SelectObject函数把先前的GDI对象选入设备描述表,以便使其恢复到原来的状态。
CClientDC dc(this);
CPen pen(PS_SOLID,1,RGB(255,0,0));
CPen* pOldPen=dc.SelectObject(&pen);
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
dc.SelectObject(pOldPen);
10.使用画刷绘图
(1)利用MFC提供的CBush类,创建画刷(画刷通常用来填充一块区域)
构造函数一:CBrush(COLORRED crColor);
CBrush brush(RGB(255,0,0));
CClientDC dc(this);
dc.FillRect(CRect(m_ptOrigin,point),&brush);
//功能:拖拽填充一块矩形区域. 这里我们只用指定的画刷填充一块区域,并不需要把画刷选入设备描述表中,设备描述表中有一个默认的白色画刷。
//CRect(POINT topLeft,POINT bottomRight);
//CDC类的成员函数FillRect(LPCRECT lpRect,CBrush* pBrush);
//lpRect:指向一个CRect对象或RECT结构体的指针。pBrush:画刷对象指针
构造函数二:CBrush(CBitmap* pBitmap);(位图画刷)
创建CBitmap对象:LoadBitmap()、CreateBitmap()、CreateBitmapIndirect()
BOOL LoadBitmap(LPCTSTR lpszResourceName);
BOOL LoadBitmap(UINT nIDResource);//资源ID
添加资源
CBitmap bitmap;
bitmap.LoadBitmap(IDB_BITMAP1);
CBrush brush(&bitmap);
CClientDC dc(this);
dc.FillRect(CRect(m_ptOrigin,point),&brush);
(2)透明画刷
1.利用CDC的Rectangle函数绘制一个矩形
CClientDC dc(this);
dc.Rectangle(CRect(m_ptOrigin,point));
//重叠时,先绘制的矩形会被遮盖一部分,这是因为设备描述表中有一个默认的白色画刷。
2.利用GetStockObject函数的NULL_BRUSH参数取得一个空的画刷。该函数返回类型是HGDIOBJECT,需要强制转换为HBRUSH类型(画刷句柄)
3.画刷句柄转化为一个画刷对象:FromHandle()函数
原型:static CDC* ASCAL FomHandle(HDC hDC);//静态成员函数,可以用类名引用
CClientDC dc(this);
CBrush *pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
CBrush *pOldBrush=dc.SelectObject(pBrush);
dc.Rectangle(CRect(m_ptOrigin,point));
dc.SelectObject(pOldBrush);
11.绘制连续线条
(1)起点(WM_LBUTTONDOWN)—鼠标移动过程中的每个点(WM_MOUSEMOVE)—终点(WM_LBUTTONUP)
(2)CTestView中添加一个布尔型私有变量m_bDraw;记录鼠标左键是否按下
(3)CTestView类构造函数中初始化m_bDraw为FALSE
(4)鼠标左键按下时,m_bDraw为TRUE(OnLButtonDown()函数中实现)
(5)鼠标左键弹起时,m_bDraw为FALSE;
(6)添加WM_MOUSEMOVE消息响应函数,在其中判断鼠标左键是否按下,并画线。(修改起点)
CClientDC dc(this);
if(m_bDraw==true)
{
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
m_ptOrigin=point; //修改起点
}
12.绘制扇形(同上,不修改起点)