MFC学习笔记 简单绘图及相关函数

1. CWnd::MessageBox 用来弹出提示消息框,可用于消息响应函数内部作为提示信息

int MessageBox( LPCTSTR lpszText, 
                    LPCTSTR lpszCaption = NULL, 
                    UINT nType = MB_OK );

plszText:指向要显示的消息的字符串类对象或者空止字符串,赋给它具体要显示的内容即可,加上双引号

lpszCaption:指向消息窗标题的字符串类对象或者空止字符串,付给它具体要显示的内容即可,加上双引号,NULL默认,显示的是工程名

nType:给消息窗设定内容和行为,这里MB_OK表示默认,还有其他行为参见MSDN

画图的方法:

Windows是依靠图形设备接口(GDI)和设备描述表(DC)对图形进行支持的。从应用程序的角度看,DC就是系统提供的一个画板,程序可以通过改变其属性的办法来进行绘图。Windows把用于改变DC属性的操作(即与绘图相关的操作)都制作成了函数,这些函数的集合就叫做GDI应用程序要向某个图形设备绘图时,应该先获得(或创建)这个图形设备的DC,然后再取得并使用合适的绘图工具对DC进行操作。

2. CWnd::OnLButtonDown / CWnd::OnLButtonUp

afx_msg void OnLButtonDown( UINT nFlags, CPoint point ); 

该函数是一个消息响应函数,当鼠标左键按下时,系统调用该函数处理窗口消息,函数体中为处理程序(afx_msg是消息标志,它向系统声明有消息映射到函数实现体,对于类向导来说.这个符号才是有意义的.它是一个消息处理函数的前缀. 类向导生成的消息函数,分发函数,事件响应函数都以这个为前缀. 如果去掉了,向导将不能识别。)

nFlags:表示是否有按键按下,有如下参数:

MK_CONTROL   Set if the CTRL key is down.

MK_LBUTTON   Set if the left mouse button is down.

MK_MBUTTON   Set if the middle mouse button is down.

MK_RBUTTON   Set if the right mouse button is down.

MK_SHIFT   Set if the SHIFT key is down. 

point:指定光标的xy坐标

假如创建的工程名为Draw,在CDrawView类添加该消息响应函数,我们在CDrawView上右击,选择添加消息处理函数,然后选择OnLButtonDown,然后依次点击Add HandlerEdit Existing.OnLButtonUpOnLButtonDown一样)

当系统接收到消息时,被传递进函数的参数即系统接收到的参数。个人理解为系统自动传递参数,不用人为设定。假如我们现在要用鼠标左键按下与松开之间时画条直线,我们首先要在鼠标左键按下时记录线条的起点,保存鼠标按下点的信息,在鼠标左键松开的时候获得终点,然后才可以画线。所以我们可以在该消息响应函数中设定以下代码(黑体字表示):

void CDrawView::OnlButtonDown(UNIT nFlags, CPoint point)
{
      // TODO: Add your message handler code here and/or call default
      m_ptOrigin=point;/*m_ptOrigin是在CDrawView类中添加的private成员变量(CPoint类型),用来保存鼠标左键按下时的点的信息*/
      CView::OnLButtonDown(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);
}

下面解释一下几个函数和成员:

HDC  GetDC(HWND hwnd); 获得一个DC的句柄,然后我们可以利用这个DC句柄用GDI函数中绘画。参数hwnd:(需要获得DC的)窗口的句柄。::表示全局SDK函数

BOOL ( HDC hdc,  // handle to device context 
         int X,   // x-coordinate of new current position
         int Y,   // y-coordinate of new current position 
         LPPOINT lpPoint   // pointer to old current position );

参数lpPoint一个指向POINT结构的指针,用来存放上一个点的位置,若此参数为NULL,则不保存上一个点的位置。成功返回1,否则0

该函数是将起点设置为刚刚鼠标左键按下时保存的点。

BOOL LineTo(
    HDC hdc,    // device context handle  
    int nXEnd,  // x-coordinate of line's ending point  
    int nYEnd   // y-coordinate of line's ending point
      );

该函数将鼠标松开的点设置为终点,并画出一条直线,成功返回1,否则0

int ReleaseDC(HWND hwnd, HDC hdc);如果释放成功返回1,否则返回0

3. MFC为我们提供了一个设备描述表的的封装类CDC,该类封装了所有与绘图相关的操作,该类提供了一个数据成员m_hDC,用来保存CDC类相关的句柄,道理与CWnd类提供的m_hWnd成员变量保存与窗口相关的窗口句柄是一样的。下面用CDC类来实现画图:

void CDrawView::OnLButtonUp(UINT nFlags, CPoint point) 
{
      // TODO: Add your message handler code here and/or call default
      CDC* pDC;//首先需要定义一个CDC类的指针
      pDC=GetDC(m_hWnd);/*利用CWnd类的成员函数GetDC获得当前窗口设备描述表对象的指针*/
      pDC->MoveTo(m_ptOrigin);//移动线条的起点
      pDC->LineTo(point);//画线
      ReleaseDC(pDC);//调用CWnd类的成员函数ReleaseDC函数释放设备描述表
      CView::OnLButtonUp(nFlags, point);
}

解释一下几个函数:

CWnd::GetDC()

CDC* GetDC();返回当前窗口设备描述表对象的指针

CWnd::ReleaseDC():

int ReleaseDC(CDC* pDC);

CDC::MoveTo()

Cpoint MoveTo(int x, int y);

Cpoint MoveTo(Cpoint point);

CDC::LineTo()

BOOL LineTo(int x, int y);

BOOL LineTo(Cpoint point);

4. MFC从通用的CDC类派生了几种特定的设备描述环境类:

CCientDC:窗口客户区的设备描述环境,但应用在WM_PAINT消息之外的消息处理函数中

CMetaFileDC:图元文件的设备描述环境,在创建可以回放的图像时使用

CPaintDC:窗口用户区的设备描述环境,在OnDraw()函数中来处理WM_PAINT消息

CWindowDC:在整个窗口内(不只是用户区)绘图的设备描述环境

其中最常用的是CPaintDC类,它代表了应用程序窗口的客户区,它只能使用在CView类的OnDraw()函数中。我们这里使用CClientDC类,它在构造时调用GetDC函数,在析构时调用ReleaseDC函数,所以我们只需要定义一个CClientDC对象就可以就可以进行画图了:

void CDrawView::OnLButtonUp(UINT nFlags, CPoint point) 
{
      // TODO: Add your message handler code here and/or call default
      CClientDC dc(this);//将this指针传递进去初始化,看下面的构造函数
      dc.MoveTo(m_ptOrigin);
      dc.LineTo(point);
      CView::OnLButtonUp(nFlags, point);
}

CClientDC的构造函数为:CClientDC(CWnd* pWnd);

pWndCWnd类的一个指针,所以我们应该传递一个CDrawView对象的指针,因为每个对象都有个this指针指向本身,所以我们可以传递一个this指针进去。

 

因为视窗口的父窗口就是框架窗口,即与CClientDC类相关联的窗口,那么假如我们想把线画到编辑栏上,我们只要将上面代码CClientDC dc(this);改成CClientDC dc(GetParent());即可。

CWnd::GetParent()

CWnd* GetParent( ) const; 返回一个CWnd类的指针。

如果我们想在整个窗口都能画线,那么我们可以用CWindowDS,它也会自动调用GetDCReleaseDC函数,只要将CClientDC dc(this);改成CWindowDC(this);即可。

如果将this改成GetDesktopWindow()将会实现在桌面窗口上画线。

5. 绘制彩色线条

以上绘制的都是黑色线条,因为设备描述表中有一个默认的黑色画笔,如果想要画出其他颜色的线条,首先需要创建一个特定颜色的画笔,然后将此画笔选入设备描述表中。我们可以用MFC提供的Cpen类来创建画笔对象,该类封装了与画笔相关的操作,有三个构造函数,其中一个是

CPen (int nPenStyle, int nWidth, COLORREF crClor);

nPenStyle指定笔的线形(实线、点线、虚线等,具体参见MSDN),nWidth表示线宽,COLORREF类型的crColor表示颜色,它用RGB这个宏来构建

COLORREF RGB (BYTE nRed, BYTE bGreen, BYTE bBlue);三个参数都是0~255之间的数字,通过数字设定颜色。

我们可以如下修改函数:

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));//创建一个对象pen:实线的、宽度1,红色的笔
      CClientDC dc(this); //创建并获得设备描述表
      CPen* oldPen=dc.SelectObject(&pen);//必须将新建的这个笔放到设备描述表中
      dc.MoveTo(m_ptOrigin);
      dc.LineTo(point);
      dc.SelectObject(oldPen);//还原原来的画笔
      CView::OnLButtonUp(nFlags, point);
}

函数说明:

CDC::SelectObject

Cpen* SelectObject(Cpen pPen);该函数是把GDI对象选入设备描述表中,并且函数返回指向原先被选对象的指针(即指向原来的画笔)。

6. 使用画刷画图(与画笔类似)

MFC提供的CBrush类,在使用之前要先创建CBrush类的对象,构造函数有两个原型:

CBrush (COLORREF color);

CBrush (int style, COLORREF color);//style 参见MSDN

所以我们可以用画刷来修改上面的程序:

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); 
      CBrush* oldBrush=dc.SelectObject(&brush);
      dc.FillRect(CRect(m_ptOrigin, point), &brush);//填充一个矩形
      dc.SelectObject(oldBrush);
      CView::OnLButtonUp(nFlags, point);
}

函数说明:

CRect(POINT topLeft, POINT bottomRight);

CDC::FillRect(LPCRECT lpRect, CBrush* pBrush);

如果现在我们想在窗口中画透明矩形,而且不能彼此覆盖,我们可以用GetStockObject函数来实现,但是GetStockObjet返回的是一个画刷的句柄,而我们需要画刷的对象,那么如何进行转换呢?CBrush类提供了一个FromHandle函数用来实现这样的功能。

void CDrawView::OnLButtonUp(UINT nFlags, CPoint point) 
{
      // TODO: Add your message handler code here and/or call default
      CClientDC dc(this); 
      CBrush*pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
      CBrush* oldBrush=dc.SelectObject(pBrush);
      dc.Rectangle(CRect(m_ptOrigin, point));
      dc.SelectObject(oldBrush);
      CView::OnLButtonUp(nFlags, point);
}

函数说明

HGDIOBJ GetStockObject(int fnObject);

备用对象的参数fnObject类型见MSDN.该函数检索预定义的备用笔、刷子、字体或者调色板的句柄。

CDC::FromHandle()

static CDC* PASCAL FromHandle(HDC hDC);返回一个对象指针

如果我们想创建一个位图画刷该如何处理呢?CBrush类有这样一个构造函数:

CBrush (CBitmap* pBitmap);用来创建位图画刷,CBitmap是一个位图类,创建CBitmap对象时,仅调用其构造函数并不能得到一个有用的位图对象,还需要调用一个初始化函数来初始化这个位图对象CBitmap类提供了多个初始化函数,如LoadBitmapCreateBitmapCreateBitmapIndirect等,这里我们用LoadBitmap来加载一幅位图。该函数声明如下:

BOOL LoadBitmap (LPCTSTR lpszResourceName);

BOOL LoadBitmap (UNIT nIDResource);

其中第二种声明需要一个资源ID作为参数,所以首先要给Draw程序增加一个位图资源,这里有两种创建一个位图的方法:

a. 单击插入(Insert) ->资源(Resource) ->选择Bitmap资源类型->单击New创建一个默认名为IDB_BITMAP1的位图资源

b. 在工具栏上的任意位置单击鼠标右键,从弹出的快捷菜单里选择资源(Resource),出现资源工具栏,然后选择位图图标即可创建了

自己画好位图后,程序如下:

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 (&bitmap);//创建位图画刷
      CClientDC dc(this); //创建并获得设备描述表
      dc.FillRect(CRect(m_ptOrigin, point), &brush);
      CView::OnLButtonUp(nFlags, point);
}

7. 绘制连续线条

为了要绘制连续的线条(即随着鼠标的移动画出连续的线条),首先需要得到线条的起点,这我们已经做到了,然后需要捕捉鼠标移动的每一个点,这可以通过鼠标移动消息(WM_MOUSEMOVE)来实现。在此消息响应中,在一次捕获的各个点之间绘制一条条很短的线段,就可以绘制成一条连续的线条。

首先我们在CDrawView类增加鼠标移动消息的响应函数(OnMouseMove),这样只要鼠标移动,就会进入这个消息响应函数中,但是我们希望鼠标左键按下后才执行,所以我们需要在这个响应函数中先判断左键是否按下了,于是,我们还要再这个类中增加一个BOOL型成员变量m_bDraw,并在CDrawView的构造函数中和初始化为FALSE,当左键按下后变为TRUE(在OnLButtonDown函数中设置,这里就不写了),鼠标松开后变为FALSE(在OnLButtonUp中设置)。另外要注意画完后要把终点赋再给起点才能实现连续。

void CDrawView::OnMouseMove(UINT nFlags, CPoint point) 
{
      // TODO: Add your message handler code here and/or call default
      CClientDC dc(this);
      CPen pen(PS_SOLID,1,RGB(255,0,0));
      CPen* oldPen=dc.SelectObject(&pen);
      if(m_bDraw==TRUE)
     {
           dc.MoveTo(m_ptOrigin);
           dc.LineTo(point);
           m_ptOrigin=point;//将终点作为下一个起点
      }
      dc.SelectObject(oldPen);
      CView::OnMouseMove(nFlags, point);
}

 

你可能感兴趣的:(MFC)