第4章 简单绘图

参考: https://blog.csdn.net/u014162133/article/details/46573873

1、

此处的MessageBox和m_hWnd都是CMainFrame的成员,所以,调用MessageBox时,不需要再传窗口句柄这一参数。

2、在单文档中,视类窗口始终覆盖在框架窗口之上,也就是说,所有操作,包括鼠标单击、鼠标移动等操作都只能由视类窗口捕获,后面的框架类窗口在覆盖区域是捕获不到的。

3、

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消息响应函数在程序中有三处:函数原型,用来关联消息和消息响应函数的宏和函数实现.

4、画线

(1) 利用SDK全局函数实现画线功能

void CLesson3View::OnLButtonUp(UINT nFlags, CPoint point)
{
    //调用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); // 在使用完设备上下文句柄后一定注意释放

    CView::OnLButtonUp(nFlags, point);
}

(2) 利用MFC的CDC类实现画线功能

void CLesson3View::OnLButtonUp(UINT nFlags, CPoint point)
{
    CDC *pDc = GetDC();
    pDc->MoveTo(m_ptnOrigin);
    pDc->LineTo(point);
    ReleaseDC(pDc);

    CView::OnLButtonUp(nFlags, point);
}

(3) 利用MFC的CClientDC类实现画线功能

void CLesson3View::OnLButtonUp(UINT nFlags, CPoint point)
{
    //使用客户区绘图类,这个是比较常用的
    // CClientDC类在构造时调用GetDC,然后在释放时又调用ReleaseDC所以不用手动释放
    //CClientDC dc(this);//CClientDC的构造函数,使用当前窗口句柄值做为参数
    CClientDC dc(GetParent());//得到关于父类窗口一个设备上下文
    dc.MoveTo(m_ptnOrigin);
    dc.LineTo(point);

    CView::OnLButtonUp(nFlags, point);
}

(4) 利用MFCCWindowDC类实现画线功能

void CLesson3View::OnLButtonUp(UINT nFlags, CPoint point)
{
    //好处是可以访问整个窗口区域,包括框架窗口客户区和非客户区,桌面等, 
    //CWindowDC dc(this);
    //CWindowDC dc(GetParent());
    CWindowDC dc(GetDesktopWindow());//这个可以画到桌面上其它地方
    dc.MoveTo(m_ptnOrigin);
    dc.LineTo(point);

    CView::OnLButtonUp(nFlags, point);
}

(5) 绘制彩色线条

void CLesson3View::OnLButtonUp(UINT nFlags, CPoint 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);//在使用完新的画笔后,要将原来的画笔重新选择时设备描述表

    CView::OnLButtonUp(nFlags, point);
}

(6) 使用画刷绘图

void CLesson3View::OnLButtonUp(UINT nFlags, CPoint point)
{
    //使用画刷来填充矩形
    CBrush m_brush(RGB(120, 0, 23));
    CClientDC dc(this);
    dc.FillRect(CRect(m_ptnOrigin, point), &m_brush);

    CView::OnLButtonUp(nFlags, point);
}
void CLesson3View::OnLButtonUp(UINT nFlags, CPoint point)
{
    CClientDC dc(this);
    dc.Rectangle(CRect(m_ptOrigin, point));

    CView::OnLButtonUp(nFlags, point);
}

(7) 位图画刷

void CLesson3View::OnLButtonUp(UINT nFlags, CPoint point)
{
    //使用位图画刷来填充矩形
    //创建一个位图对象
    CBitmap m_bitmap;
    m_bitmap.LoadBitmap(IDB_MyBitmap);
    CBrush m_Brush(&m_bitmap);
    CClientDC dc(this);
    dc.FillRect(CRect(m_ptnOrigin, point), &m_Brush);

    CView::OnLButtonUp(nFlags, point);
}

(8) 透明画刷

void CLesson3View::OnLButtonUp(UINT nFlags, CPoint point)
{
    //透明画刷
    //首先使用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);

    CView::OnLButtonUp(nFlags, point);
}

(9) 类的静态成员函数可以由类名直接调用,也可以由对象调用。静态成员函数并不属于某个对象,它属于类本身。程序运行时,即使没有实例化类的对象,静态成员函数和静态成员变量已有其内存空间。静态成员函数不能访问非静态成员函数或变量!静态成员变量必须在类的外部初始化。非静态成员函数或变量可以访问静态成员变量或函数。 

(10) 理解代码区,数据区,堆,栈!(http://www.downcode.com/server/j_server/J_1010.Html)

对于一个进程的内存空间而言,可以在逻辑上分成3个部份:代码区,静态数据区和动态数据区。动态数据区一般就是“堆栈”。“栈(stack)”和“堆(heap)”是两种不同的动态数据区,栈是一种线性结构,堆是一种链式结构。进程的每个线程都有私有的“栈”,所以每个线程虽然代码一样,但本地变量的数据都是互不干扰。一个堆栈可以通过“基地址”和“栈顶”地址来描述。全局变量和静态变量分配在静态数据区,本地变量分配在动态数据区,即堆栈中。程序通过堆栈的基地址和偏移量来访问本地变量。

原文地址:https://www.cnblogs.com/happykoukou/p/8921250.html