转自:
1.http://blog.sina.com.cn/s/blog_6b5180bf01012kbz.html
2.http://blog.csdn.net/happyhhb/article/details/1623278
3.http://njufsh.blog.163.com/blog/static/1917928162011103104222589/
孙鑫的MFC教程第4课主要讲了消息机制和MFC作图。
MFC使用一种消息映射机制来处理消息,在应用程序框架中的表现就是一个消息与消息处理函数一一对应的消息映射表,以及消息处理函数的声明和实现等代码。当窗口接收到消息时,会到消息映射表中查找该消息对应的消息处理函数,然后由消息处理函数进行相应的处理。SDK编程时需要在窗口过程中一一判断消息值进行相应的处理,相比之下MFC的消息映射机制要方便好用的多。
mfc在后台维护了一个句柄,以及程序各个类的句柄映射表,当我门在某个窗口上操作产生消息,该消息携带一个该窗口的句柄,通过该句柄找到该对象的指针,由窗口类传至父类,再由父类通过消息循环调用一个CWnd::WindowProc()
WindowProc()是一个虚函数,每个从CWnd继承的子类都有这样一个虚函数
WindowProc()调用了一个OnWndMsg(),该函数完成了主要的消息映射的处理
OnWndMsg()会辨别消息的种类,随后根据传过来的隐含的this指针来到类.h消息宏(DECLARE_MESSAGE_MAP()之上)查找有无对应的消息处理函数原形的声明,然后到类.cpp中的消息映射表(BEGIN_MESSAGE_MAP()和END_MESSAGE_MAP())中寻找有没有对应的处理函数,最终调用消息处理函数
消息响应会在3处修改代码
第一处是在头文件中
//{{AFX_MSG(CDrawView) afx_msg void OnLButtonDown(UINT nFlags, CPoint point); afx_msg void OnLButtonUp(UINT nFlags, CPoint point); afx_msg void OnMouseMove(UINT nFlags, CPoint point); //}}AFX_MSG DECLARE_MESSAGE_MAP()
另一处是cpp文件的begin MessageMap和End MessageMap之间,
BEGIN_MESSAGE_MAP(CDrawView, CView) //{{AFX_MSG_MAP(CDrawView) ON_WM_LBUTTONDOWN() ON_WM_LBUTTONUP() ON_WM_MOUSEMOVE() //}}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()
最后是要有函数实现的代码。
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); }
利用HDC来作图
用菜单命令增加私有成员变量CPoint m_ptOrigin=0; 初始化为0,响应鼠标左键按下函数为:
void CDrawView::OnLButtonDown(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default // 按下去的时候是起点 m_ptOrigin = point; CView::OnLButtonDown(nFlags, point); }
用菜单命令增加鼠标左键up响应函数
void CDrawView::OnLButtonUp(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default CView::OnLButtonUp(nFlags, point); }
这里面的点就是画线的终点;改变代码如下:
void CDrawView::OnLButtonUp(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default //弹起来的时候是终点 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);
CView::OnLButtonUp(nFlags, point); }
以上我们采用了API函数,也就是全局函数来完成的;
API函数和类的成员函数都有ReleaseDC函数,所以调用API函数就要特别用作用域符号注明是API函数,如果不同名,则可以直接用;
用CDC类画线
MFC中所有画图的功能都集成到了CDC这个类中:
用CDC类绘制直线;
仍旧在View类里面捕获消息:
注意:画线的代码发生变化,其余步骤不变;
直接修改鼠标左键up响应函数
void CDrawView::OnLButtonUp(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default //弹起来的时候是终点 CDC *pDC=GetDC(); //将设备移动到起点 pDC->MoveTo(m_ptOrigin); pDC->LineTo(point); //释放设备 ReleaseDC(pDC); CView::OnLButtonUp(nFlags, point); }
以上我们采用了CDC类画线,看起来很简洁;
采用CClientDC画线
CClientDC类是从CDC这个类派生出来的;
优点:构造函数中调用GetDC;析构函数调用ReleaseDC;
也就是说用CClientDC类,就不用显示调用DetDC和ReleaseDC函数;
CClientDC绘制直线;
仍旧在View类里面捕获消息:
注意:画线的代码发生变化,其余步骤不变;
直接修改鼠标左键up响应函数
void CDrawView::OnLButtonUp(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default //弹起来的时候是终点 CClientDC dc(this); //将设备移动到起点 dc.MoveTo(m_ptOrigin); dc.LineTo(point); CView::OnLButtonUp(nFlags, point); }
以上我们采用了CClientDC类画线,看起来更简洁;
用this指针传递,表示在CDrawView上做图;
下面 我想和CMainFrame类相关,如何获取父窗口的指针?
void CDrawView::OnLButtonUp(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default //弹起来的时候是终点 CClientDC dc(GetParent());//父类的,会在框架窗口,会画在工具栏上。 //将设备移动到起点 dc.MoveTo(m_ptOrigin); dc.LineTo(point); CView::OnLButtonUp(nFlags, point); }
采用CWindowDC画线
CWindowDC类是从CDC这个类派生出来的;
优点:构造函数中调用GetDC;析构函数调用ReleaseDC;
也就是说用CClientDC类,就不用显示调用DetDC和ReleaseDC函数;
可以访问整个屏幕区域,包括客户区和非客户区;
CWindowDC绘制直线;
仍旧在View类里面捕获消息:
注意:画线的代码发生变化,其余步骤不变;
直接修改鼠标左键up响应函数
void CDrawView::OnLButtonUp(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default //弹起来的时候是终点 CWindowDC dc(GetParent()); //将设备移动到起点 dc.MoveTo(m_ptOrigin); dc.LineTo(point); CView::OnLButtonUp(nFlags, point); }
在标题栏+菜单栏上(非客户区)可以访问整个屏幕。
能否画到vc上,或者桌面上?
桌面本身就是一个窗口。
void CDrawView::OnLButtonUp(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default //弹起来的时候是终点 CWindowDC dc(GetDesktopWindow()); //将设备移动到起点 dc.MoveTo(m_ptOrigin); dc.LineTo(point); CView::OnLButtonUp(nFlags, point); }
则可以画到vc上,或者桌面上;
CPen修改颜色
★如何画其他颜色的线条?
仍旧在View类里面捕获消息:
注意:画线的代码发生变化,其余步骤不变;
RGB这个宏 参数三个 红绿蓝 三个值的改变来调整颜色。
直接修改鼠标左键up响应函数
void CDrawView::OnLButtonUp(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default //弹起来的时候是终点 CPen pen(PS_SOLID, 1, RGB(255, 0, 0)); // 创建一个画笔 CClientDC dc(this); CPen *pOldPen=dc.SelectObject(&pen); //这个笔选在设备表,返回的是原先的CPen //将设备移动到起点 dc.MoveTo(m_ptOrigin); //移动到原点。 dc.LineTo(point); //终点 dc.SelectObject(pOldPen); //先前的画笔选择 CView::OnLButtonUp(nFlags, point); }
这是画出红色的直线;
阴影线 粗细只能1或者更小
以下代码是设置新笔,保存旧的笔;
CPen *pOldPen=dc.SelectObject(&pen);
CBrush填充区域
★如何矩形区域区域颜色?
CBrush填充区域
仍旧在视类里面捕获消息:
注意:画线的代码发生变化,其余步骤不变;
直接修改鼠标左键up响应函数
void CDrawView::OnLButtonUp(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default CBrush brush(RGB(255,0,0)); CClientDC dc(this); dc.FillRect(CRect(m_ptOrigin, point), &brush); //用指定的画刷来填充指定的矩形 ,当然也有自己缺省的画刷。 //不用将brush选中在设备描述表中。都是指定的!! 指定的画刷,指定的矩形区域。
CView::OnLButtonUp(nFlags, point); }
这是画出红色的矩形;
位图画刷
首先要添加一个位图资源,insert菜单里面resource,添加new了一个Bitmap,它的资源ID是IDB_BITMAP1,程序如下:
void CDrawView::OnLButtonUp(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default CBitmap bitmap; bitmap.LoadBitmap(IDB_BITMAP1); //加载这幅位图。 CBrush brush(&bitmap); //创建位图的画刷,位图对象的指针。 CClientDC dc(this); dc.FillRect(CRect(m_ptOrigin, point), &brush); CView::OnLButtonUp(nFlags, point); }
透明画刷
★如何矩形区域不相互遮挡?
CBrush透明画刷
仍旧在视类里面捕获消息:
注意:画线的代码发生变化,其余步骤不变;
直接修改鼠标左键up响应函数
void CDrawView::OnLButtonUp(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default CBrush *pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));//获取透明画刷对象指针 CClientDC dc(this); CBrush *pOldBrush=dc.SelectObject(pBrush); //将透明画刷选入DC dc.Rectangle(CRect(m_ptOrigin,point)); //画矩形 dc.SelectObject(pOldBrush); //释放透明画刷 CView::OnLButtonUp(nFlags, point); }
画出的矩形相互不遮挡,也就是透明的;
★★★★★注意点:
1)静态方法不属于某一个具体对象,而属于类本身,在类加载的时候就已经为类静态方法分配了代码去,故可用CBrush::FromHandle()形式调用。非静态的方法,是属于某个对象的。
2)静态方法中,不能引用非静态的数据成员和方法。
3)静态数据成员需要在类外单独做初始化,形式如: 变量类型 类名::变量名=初始值;
绘制曲线
★如何绘制拖动的曲线?
绘制拖动的曲线
第一步:增加一个BOOL m_bDraw;
在构造函数中m_bDraw=FALSE;
鼠标左键按下down响应函数中设置m_bDraw=TRUE;
用这个变量表示鼠标按下;
CDrawView::CDrawView() { // TODO: add construction code here m_ptOrigin=0; m_bDraw=FALSE; //鼠标起来的时侯,构造函数初始化 }
void CDrawView::OnLButtonDown(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default // 按下去的时候是起点 m_ptOrigin = point; m_bDraw=TRUE; 鼠标按下去的时候 CView::OnLButtonDown(nFlags, point); }
增加鼠标左键move响应函数,用右击菜单栏添加
void CDrawView::OnMouseMove(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default CClientDC dc(this); if(m_bDraw==TRUE) { dc.MoveTo(m_ptOrigin); //移动到原点,就是第一次按下去的时候 dc.LineTo(point); m_ptOrigin=point; //下一个起点就是上一个终点 } CView::OnMouseMove(nFlags, point); }