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:指定光标的x和y坐标
假如创建的工程名为Draw,在CDrawView类添加该消息响应函数,我们在CDrawView上右击,选择添加消息处理函数,然后选择OnLButtonDown,然后依次点击Add Handler和Edit Existing.(OnLButtonUp与OnLButtonDown一样)
当系统接收到消息时,被传递进函数的参数即系统接收到的参数。个人理解为系统自动传递参数,不用人为设定。假如我们现在要用鼠标左键按下与松开之间时画条直线,我们首先要在鼠标左键按下时记录线条的起点,保存鼠标按下点的信息,在鼠标左键松开的时候获得终点,然后才可以画线。所以我们可以在该消息响应函数中设定以下代码(黑体字表示):
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);
pWnd是CWnd类的一个指针,所以我们应该传递一个CDrawView对象的指针,因为每个对象都有个this指针指向本身,所以我们可以传递一个this指针进去。
因为视窗口的父窗口就是框架窗口,即与CClientDC类相关联的窗口,那么假如我们想把线画到编辑栏上,我们只要将上面代码CClientDC dc(this);改成CClientDC dc(GetParent());即可。
CWnd::GetParent():
CWnd* GetParent( ) const; 返回一个CWnd类的指针。
如果我们想在整个窗口都能画线,那么我们可以用CWindowDS,它也会自动调用GetDC和ReleaseDC函数,只要将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类提供了多个初始化函数,如LoadBitmap,CreateBitmap,CreateBitmapIndirect等,这里我们用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);
}