1、标准消息映射机制
在每个能接受和处理消息的类中定义一个消息和消息处理函数对照表,即消息映射表。在消息映射表中,消息与对应的消息处理函数是成对出现的。当有消息要处理时,程序即在消息映射表中搜索是否有该消息,如有有该消息则调用其对应的消息处理函数。
在视类中添加WM_LBUTTONDOWN消息处理函数后,会增加三处代码:
一是在视类头文件中DECLARE_MESSAGE_MAP宏下面添加了消息响应函数的声明,以afx_msg宏开头:
// 生成的消息映射函数
protected:
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
二是在视类源文件中BEGIN_MESSAGE_MAP宏与END_MESSAGE_MAP之间的消息映射表中添加了消息映射宏ON_WM_LBUTTONDOWN,这个宏的作用就是把WM_LBUTTONDOWN消息与消息响应函数OnLButtonDown关联起来:
BEGIN_MESSAGE_MAP(Ctest2View, CView)
// 标准打印命令
ON_COMMAND(ID_FILE_PRINT, &CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, &CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW, &CView::OnFilePrintPreview)
ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()
三是在视类源文件中添加了消息响应函数的定义:
void Ctest2View::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
CView::OnLButtonDown(nFlags, point);
}
2、绘制线条
为了在窗口上进行绘图操作,必须获得与这个窗口相关联的设备描述表(DC)。
0、CDC::SetPixel()功能为在指定的点绘制一个指定颜色的像素。
1、利用Windows函数绘图
void Ctest2View::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
m_ptOrigin = point;//保存鼠标左键按下的起点
CView::OnLButtonDown(nFlags, point);
}
void Ctest2View::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
HDC hdc = ::GetDC(m_hWnd);//获得视类窗口的设备描述表
MoveToEx(hdc, m_ptOrigin.x, m_ptOrigin.y, NULL);//移动到线条的起点
LineTo(hdc, point.x, point.y);//画线
::ReleaseDC(m_hWnd, hdc);//释放DC资源
CView::OnLButtonUp(nFlags, point);
}
2、利用MFC的CDC类对象绘图
void Ctest2View::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
CDC* pDC = GetDC();//获得与视类窗口相关联的CDC对象
pDC->MoveTo(m_ptOrigin);
pDC->LineTo(point);
ReleaseDC(pDC);
CView::OnLButtonUp(nFlags, point);
}
3、利用MFC的CClientDC类对象绘图,
MFC提供的CClientDC类派生于CDC类,用来在窗口的客户区绘图,并且在构造时调用GetDC()获得与指定窗口相关联的设备描述表,在析构时调用Release()释放DC资源,使用方便。
void Ctest2View::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
CClientDC dc(this);//获得与视类窗口相关联的CClientDC对象
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
CView::OnLButtonUp(nFlags, point);
}
4、利用MFC的CWindowDC类对象绘图,CClientDC类对象只能在框架类窗口的客户区(视类区域+工具栏区域)画图,CWindowDC可以实现在窗口的整个区域画图。
void Ctest2View::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
CWindowDC dc(GetParent());//获得与视类窗口相关联的CWindowDC对象
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
CView::OnLButtonUp(nFlags, point);
}
GetDesktopWindow()函数可以获得Windows桌面窗口的句柄。以下代码可以实现在桌面上画线:
void Ctest2View::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
CWindowDC dc(GetDesktopWindow());//获得与视类窗口相关联的CWindowDC对象
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
CView::OnLButtonUp(nFlags, point);
}
线条的颜色、线宽、类型(虚线、实线等)是由当前DC中的画笔决定的,如果想要设置线条的颜色、粗细、类型则需要自定义一个CPen画笔对象,然后将该画笔对象选入到当前设备描述表中,完成绘图后应将先前的画笔对象恢复到设备描述表中;CDC::SelectObject()可以将画笔对象选入到当前DC中。
CDC::SelectObject()函数可以将指定的GDI对象(画刷CBrush、画笔CPen、字体CFont、位图CBitmap、调色板等)选入到当前设备中。
eg:
void Ctest2View::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
CPen pen(PS_SOLID, 1, RGB(255,0,0));//创建一个实线线条,宽度为1,红色的画笔对象
CWindowDC dc(this);
CPen* pOldPen = dc.SelectObject(&pen);//将画笔对象选入到设备描述表中
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
dc.SelectObject(pOldPen);//恢复设备描述表
CView::OnLButtonUp(nFlags, point);
}
若想要虚线线条则为PS_DASH,且宽度应小于等于1;绘制点线为PS_DOT。
3、使用画刷绘图
画刷通常用来填充一块区域,CBrush为画刷类。下边代码功能是利用一个红色画刷填充鼠标拖拽过程中形成的一块矩形:
void Ctest2View::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
CBrush brush(RGB(255,0,0));//定义一个红色画刷
CClientDC dc(this);
dc.FillRect(CRect(m_ptOrigin,point), &brush);//使用红色画刷绘制一块矩形区域
CView::OnLButtonUp(nFlags, point);
}
CDC::FillRect()功能为使用指定的画刷绘制一块矩形区域。
这里只是利用指定的画刷填充一块区域,因此不需要把指定的画刷选入设备描述表中,设备描述表中存在一个默认的白色画刷。
可以使用位图画刷填充鼠标拖拽过程中形成的一块矩形区域:
void Ctest2View::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
CBitmap bitmap;//定义位图对象
bitmap.LoadBitmapW(IDB_BITMAP1);//初始化位图对象
CBrush brush(&bitmap);//定义一个位图画刷
CClientDC dc(this);
dc.FillRect(&CRect(m_ptOrigin, point), &brush);//使用位图画刷绘制一块矩形区域
CView::OnLButtonUp(nFlags, point);
}
CDC::Rectangle()功能为使用默认画刷(通常为白色)绘制一块矩形区域。
CDC::Ellipse()功能为使用默认画刷(通常为白色)绘制一块椭圆区域。如果不想使用默认的白色画刷,而是想要实现透明画刷效果,则可以将透明画刷选入到当前DC中:
void Ctest2View::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
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);//还原设备描述表
CView::OnLButtonUp(nFlags, point);
}
GetStockObject(NULL_BRUSH)用来获得系统透明画刷,CBrush::FromHandle()为画刷类的静态成员函数,其可以将画刷句柄转换为画刷对象指针,CDC::SelectObject()可以将指定画刷选入到当前DC中。
4、绘制连续线条
void Ctest2View::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
m_ptOrigin = point;//保存鼠标左键按下的起点
m_bLBtnDown = true;//鼠标左键按下
CView::OnLButtonDown(nFlags, point);
}
void Ctest2View::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
CClientDC dc(this);
if(m_bLBtnDown)//鼠标左键按下
{
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
m_ptOrigin = point;//终点作为下一个小线段的起点
}
CView::OnMouseMove(nFlags, point);
}
void Ctest2View::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
m_bLBtnDown = false;//鼠标左键弹开
CView::OnLButtonUp(nFlags, point);
}
如果保持每个小线段的起点不变,即以鼠标左键按下为起点不变,即注释掉20行代码则就为绘制出扇形效果的线条:
void Ctest2View::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
m_ptOrigin = point;//保存鼠标左键按下的起点
m_bLBtnDown = true;//鼠标左键按下
CView::OnLButtonDown(nFlags, point);
}
void Ctest2View::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
CClientDC dc(this);
if(m_bLBtnDown)//鼠标左键按下
{
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
}
CView::OnMouseMove(nFlags, point);
}
void Ctest2View::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
m_bLBtnDown = false;//鼠标左键弹开
CView::OnLButtonUp(nFlags, point);
}
如果想要绘制一个带边线的扇形,就需要为视类再增加一个成员变量m_ptOld用来保存上一个移动点,在OnMouseMove()中初始化:m_ptOld = point;。并在OnMouseMove()中添加dc.LineTo(m_ptOld);实现从鼠标当前点到上一个点的连线,也就是绘制了一条小边线。然后保存当前鼠标点为m_ptOld:m_ptOld = point; 为画下一条小边线做准备。
void Ctest2View::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
m_ptOrigin = point;//保存鼠标左键按下的起点
m_bLBtnDown = true;//鼠标左键按下
m_ptOld = point;
CView::OnLButtonDown(nFlags, point);
}
void Ctest2View::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
CClientDC dc(this);
if(m_bLBtnDown)//鼠标左键按下
{
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
dc.LineTo(m_ptOld);//从point到m_ptOld画线
m_ptOld = point;
}
CView::OnMouseMove(nFlags, point);
}
void Ctest2View::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
m_bLBtnDown = false;//鼠标左键弹开
CView::OnLButtonUp(nFlags, point);
}
5、设置绘图模式
CDC::SetROP2(int nDrawMode)用来设置绘图的像素,nDrawMode取值意义:
R2_WHITE:设置绘图像素为白色
R2_BLACK:设置绘图像素为黑色
R2_NOP:任何绘制将不改变当前的状态
R2_NOT:设置绘图像素为屏幕颜色的反色
R2_COPYPEN:设置绘图像素为笔的颜色
R2_NOTCOPYPEN:设置绘图像素为笔颜色的反色
R2_XORPEN:设置绘图像素为画笔颜色异或屏幕颜色
R2_NOTXORPEN:设置绘图像素为画笔颜色异或屏幕颜色后再取反
R2_MERGENOTPEN:设置绘图像素为笔颜色的反色与屏幕颜色进行或操作
R2_NOTXORPEN这种绘画模式是这样的,它先把画笔颜色与屏幕颜色异或,异或之后再取反最后得到一个颜色值显示在屏幕上。举个例子,你使用R2_NOTXORPEN这种绘画模式,你用红色画笔在黑色背景上画一条直线,显示红色,但你再用这只笔在刚画的直线上重画一遍,就相当于把开始画的红线擦除掉了,划线的地方显示为背景色。
R2_NOT绘画模式同样有在同一个地方画两次相当于什么都没画的功能,不过R2_NOT绘画模式第一次画的时候显示颜色并不是你选定的画笔颜色,而是屏幕颜色的反色。
下面这个例子为绘制红色直线,当绘制下一条直线的时候利用SetROP2(R2_NOTXORPEN)改变绘图像素,将上次绘制的直线擦除:
void Ctest2View::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
CClientDC dc(this);
CPen pen(PS_SOLID, 5, RGB(255,0,0));
CPen* pOldPen = dc.SelectObject(&pen);
dc.SetROP2(R2_NOTXORPEN);
dc.MoveTo(m_ptOrigin);
dc.LineTo(m_ptEnd);
m_ptOrigin = point;
dc.SelectObject(pOldPen);
CView::OnLButtonDown(nFlags, point);
}
void Ctest2View::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
CClientDC dc(this);
CPen pen(PS_SOLID, 5, RGB(255,0,0));
CPen* pOldPen = dc.SelectObject(&pen);
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
m_ptEnd = point;
dc.SelectObject(pOldPen);
CView::OnLButtonUp(nFlags, point);
}