1.创建图形
新建一个MFC单文档应用程序,仍然增加一个菜单,命名绘图,再添加几个菜单项:
IDM_POINT(点)、IDM_LINE(直线)、IDM_ RECTANGLE(矩形)、IDM_ ELLIPSE (椭圆),并分别对这四个菜单项添加命令响应,并在CGraphic2View类中添加一个私有的成员变量UINT m_nDrawType;
并在构造方法中初始化:
CGraphic2View::CGraphic2View()
{
// TODO: add construction code here
m_nDrawType=0;
m_ptOrigin=0;
m_dcMetaFile.Create();//创建一个内存的CMetaFileDC对象
}
再编辑四个菜单项的命令响应:
void CGraphic2View::OnPoint()
{
// TODO: Add your command handler code here
m_nDrawType=1;
}
void CGraphic2View::OnLine()
{
m_nDrawType=2;
}
void CGraphic2View::OnRectangle()
{
m_nDrawType=3;
}
void CGraphic2View::OnEllipse()
{
m_nDrawType=4;
}
再在CGraphic2View类中添加一个私有的成员变量m_ptOrigin用来保存原点坐标,并在构造函数中初始化,上面已列出。
接着再给CGraphic2View类添加两个WM_LBUTTONDOWN和WM_LBUTTONUP消息响应函数,编辑:
void CGraphic2View::OnLButtonDown(UINT nFlags, CPoint point)
{
m_ptOrigin=point;//将这个点保存起来
CView::OnLButtonDown(nFlags, point);
}
void CGraphic2View::OnLButtonUp(UINT nFlags, CPoint point)
{
CClientDC dc(this);
CBrush *pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
//创建透明画刷
dc.SelectObject(pBrush);
}
switch(m_nDrawType)
{
case 1:
dc.SetPixel(point,RGB(0,0,0)); break;
case 2:
dc.MoveTo(m_ptOrigin);
dc.LineTo(point); break;
case 3:
dc.Rectangle(CRect(m_ptOrigin,point)); break;
case 4:
dc.Ellipse(CRect(m_ptOrigin,point)); break;
}
CView::OnLButtonUp(nFlags, point);
}
运行可以发现当窗口尺寸发现变化是,所绘制的图形也消失了!
2.图形重绘
新建一个Generic Class (name:CGraph),并在这个类当中增加3个成员变量,同时为了对这3个成员进行赋值,定义一个带参数的构造函数,如下:
public:
CPoint m_ptOrigin;
CPoint m_ptEnd;
UINT m_nDrawType;
CGraph();
CGraph(UINT m_nDrawType,CPoint m_ptOrigin,CPoint m_ptEnd);
并在源文件Graph.cpp当中,对这个类的成员变量赋值:
CGraph::CGraph(UINT m_nDrawType,CPoint m_ptOrigin,CPoint m_ptEnd)
{
//对这个类的成员变量赋值
this->m_nDrawType=m_nDrawType;//图形类型
this->m_ptOrigin=m_ptOrigin;//图形原点
this->m_ptEnd=m_ptEnd;//图形终点
}
在CGraphic2View类当中,新增加一个成员变量CPtrArray m_ptrArray; 这个集合类型的变量用来保存CGraph对象,并在CGraphic2View::OnLButtonUp添加:
CGraph graph(m_nDrawType,m_ptOrigin,point);//构造一个对象
//注:此时graph是一个局部变量,为它分配的内存在栈中,当这个函数结束时,对象也会被析构
m_ptrArray.Add(&graph);//将graph对象保存到集合类的对象m_ptArray当中
接下来在CGraphic2View::OnDraw函数中添加:
CBrush *pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
//创建透明画刷
pDC->SelectObject(pBrush);
//将保存的图形对象取出来
for(int i=0;i<m_ptrArray.GetSize();i++)
{
switch(((CGraph*)m_ptrArray.GetAt(i))->m_nDrawType)
{
case 1:
pDC->SetPixel(((CGraph*)m_ptrArray.GetAt(i))->m_ptEnd,RGB(0,0,0));
break;
case 2:
pDC->MoveTo(((CGraph*)m_ptrArray.GetAt(i))->m_ptOrigin);
pDC->LineTo(((CGraph*)m_ptrArray.GetAt(i))->m_ptEnd);
break;
case 3:
pDC->Rectangle(CRect(((CGraph*)m_ptrArray.GetAt(i))->m_ptOrigin,
((CGraph*)m_ptrArray.GetAt(i))->m_ptEnd));
break;
case 4:
pDC->Ellipse(CRect(((CGraph*)m_ptrArray.GetAt(i))->m_ptOrigin,
((CGraph*)m_ptrArray.GetAt(i))->m_ptEnd));
break;
default:
break;
}
}
运行,当改变窗口大小时,图像仍然消息了,下面继续修改。
在CGraphic2View::OnLButtonUp中修改:
//CGraph graph(m_nDrawType,m_ptOrigin,point);//构造一个对象
//注:此时graph是一个局部变量,为它分配的内存在栈中,当这个函数结束时,对象也会被析构
//m_ptrArray.Add(&graph);//将graph对象保存到集合类的对象m_ptArray当中
CGraph *pGraph;//这也是一个局部的指针变量,内存在栈中
pGraph=new CGraph(m_nDrawType,m_ptOrigin,point);
//这一句调用它的构造函数为pGraph指针类型的变量在堆中分配一个内存空间,构造一个CGraph类的对象
//因为用new所分配的内存都是在堆中分配的,在堆中分配的对象的内存,如果不去显示的调用Delete去释放
//这个对象内存,那么这个对象的生命周期是和应用程序保持一致的
m_ptrArray.Add(pGraph);//将之前在堆中构造的CGraph类的对象地地址索引保存到集合类的对象m_ptArray当中
运行,绘制图形,改变窗口大小,图形没有消失,OK!
3.将图形输出放置到WM_PAINT消息响应函数中
在CGraphic2View类上添加一个WM_PAINT消息处理,编辑:
void CGraphic2View::OnPaint()
{
CPaintDC dc(this); // device context for painting
// TODO: Add your message handler code here
OnPrepareDC(&dc); //调整显示上下文的属性
OnDraw(&dc);//调用OnDraw
// Do not call CView::OnPaint() for painting messages
}
4.给窗口添加滚动条
首先将CGraphic2View的基类CView手动改成CScorllView,在CGraphic2View的头文件中和源文件中将所有的CView都替换成CScrollView,编译运行,会发现出现了非法操作,
先介绍一下坐标空间的转换,下图
Coordinate Spaces and Transformations
Applications use coordinate spaces and transformations to scale, rotate, translate, shear, and reflect graphics output. Acoordinate space is a planar space that locates two-dimensional objects by using two reference axes that are perpendicular to each other. There are four coordinate spaces: world, page, device, and physical device (client area, desktop, or page of printer paper).
A transformation is an algorithm that alters ("transforms") the size, orientation, and shape of objects. Transformations also transfer a graphics object from one coordinate space to another. Ultimately, the object appears on the physical device, which is usually a screen or printer.
一个例子:
Using Coordinate Spaces and Transformations
This section contains an example that demonstrates the following tasks:
The following example was used to create the illustrations that appear earlier in this overview.
void TransformAndDraw(int iTransform, HWND hWnd)
{
HDC hDC;
XFORM xForm;
RECT rect;
// Retrieve a DC handle for the application's window.
hDC = GetDC(hWnd);
// Set the mapping mode to LOENGLISH. This moves the
// client area origin from the upper left corner of the
// window to the lower left corner (this also reorients
// the y-axis so that drawing operations occur in a true
// Cartesian space). It guarantees portability so that
// the object drawn retains its dimensions on any display.
SetGraphicsMode(hDC, GM_ADVANCED);
SetMapMode(hDC, MM_LOENGLISH);
// Set the appropriate world transformation (based on the
// user's menu selection).
switch (iTransform)
{
case SCALE: // Scale to 1/2 of the original size.
xForm.eM11 = (FLOAT) 0.5;
xForm.eM12 = (FLOAT) 0.0;
xForm.eM21 = (FLOAT) 0.0;
xForm.eM22 = (FLOAT) 0.5;
xForm.eDx = (FLOAT) 0.0;
xForm.eDy = (FLOAT) 0.0;
SetWorldTransform(hDC, &xForm);
break;
case TRANSLATE: // Translate right by 3/4 inch.
xForm.eM11 = (FLOAT) 1.0;
xForm.eM12 = (FLOAT) 0.0;
xForm.eM21 = (FLOAT) 0.0;
xForm.eM22 = (FLOAT) 1.0;
xForm.eDx = (FLOAT) 75.0;
xForm.eDy = (FLOAT) 0.0;
SetWorldTransform(hDC, &xForm);
break;
case ROTATE: // Rotate 30 degrees counterclockwise.
xForm.eM11 = (FLOAT) 0.8660;
xForm.eM12 = (FLOAT) 0.5000;
xForm.eM21 = (FLOAT) -0.5000;
xForm.eM22 = (FLOAT) 0.8660;
xForm.eDx = (FLOAT) 0.0;
xForm.eDy = (FLOAT) 0.0;
SetWorldTransform(hDC, &xForm);
break;
case SHEAR: // Shear along the x-axis with a
// proportionality constant of 1.0.
xForm.eM11 = (FLOAT) 1.0;
xForm.eM12 = (FLOAT) 1.0;
xForm.eM21 = (FLOAT) 0.0;
xForm.eM22 = (FLOAT) 1.0;
xForm.eDx = (FLOAT) 0.0;
xForm.eDy = (FLOAT) 0.0;
SetWorldTransform(hDC, &xForm);
break;
case REFLECT: // Reflect about a horizontal axis.
xForm.eM11 = (FLOAT) 1.0;
xForm.eM12 = (FLOAT) 0.0;
xForm.eM21 = (FLOAT) 0.0;
xForm.eM22 = (FLOAT) -1.0;
xForm.eDx = (FLOAT) 0.0;
xForm.eDy = (FLOAT) 0.0;
SetWorldTransform(hDC, &xForm);
break;
case NORMAL: // Set the unity transformation.
xForm.eM11 = (FLOAT) 1.0;
xForm.eM12 = (FLOAT) 0.0;
xForm.eM21 = (FLOAT) 0.0;
xForm.eM22 = (FLOAT) 1.0;
xForm.eDx = (FLOAT) 0.0;
xForm.eDy = (FLOAT) 0.0;
SetWorldTransform(hDC, &xForm);
break;
}
// Find the midpoint of the client area.
GetClientRect(hWnd, (LPRECT) &rect);
DPtoLP(hDC, (LPPOINT) &rect, 2);
// Select a hollow brush.
SelectObject(hDC, GetStockObject(HOLLOW_BRUSH));
// Draw the exterior circle.
Ellipse(hDC, (rect.right / 2 - 100), (rect.bottom / 2 + 100),
(rect.right / 2 + 100), (rect.bottom / 2 - 100));
// Draw the interior circle.
Ellipse(hDC, (rect.right / 2 -94), (rect.bottom / 2 + 94),
(rect.right / 2 + 94), (rect.bottom / 2 - 94));
// Draw the key.
Rectangle(hDC, (rect.right / 2 - 13), (rect.bottom / 2 + 113),
(rect.right / 2 + 13), (rect.bottom / 2 + 50));
Rectangle(hDC, (rect.right / 2 - 13), (rect.bottom / 2 + 96),
(rect.right / 2 + 13), (rect.bottom / 2 + 50));
// Draw the horizontal lines.
MoveToEx(hDC, (rect.right/2 - 150), (rect.bottom / 2 + 0), NULL);
LineTo(hDC, (rect.right / 2 - 16), (rect.bottom / 2 + 0));
MoveToEx(hDC, (rect.right / 2 - 13), (rect.bottom / 2 + 0), NULL);
LineTo(hDC, (rect.right / 2 + 13), (rect.bottom / 2 + 0));
MoveToEx(hDC, (rect.right / 2 + 16), (rect.bottom / 2 + 0), NULL);
LineTo(hDC, (rect.right / 2 + 150), (rect.bottom / 2 + 0));
// Draw the vertical lines.
MoveToEx(hDC, (rect.right/2 + 0), (rect.bottom / 2 - 150), NULL);
LineTo(hDC, (rect.right / 2 + 0), (rect.bottom / 2 - 16));
MoveToEx(hDC, (rect.right / 2 + 0), (rect.bottom / 2 - 13), NULL);
LineTo(hDC, (rect.right / 2 + 0), (rect.bottom / 2 + 13));
MoveToEx(hDC, (rect.right / 2 + 0), (rect.bottom / 2 + 16), NULL);
LineTo(hDC, (rect.right / 2 + 0), (rect.bottom / 2 + 150));
ReleaseDC(hWnd, hDC);
}
逻辑坐标与设备坐标的相互转换
DMM_TEXT映射方式下逻辑坐标与设备坐标的相互转换
接下来,在CGraphic2View类上添加一个虚函数,编辑:
void CGraphic2View::OnInitialUpdate()
{
CView::OnInitialUpdate();
// TODO: Add your specialized code here and/or call the base class
SetScrollSizes(MM_TEXT,CSize(1000,800));//设置一个滚动条
}
运行,可以发现多了滚动条,当把窗口滚动条拖到最下方时,再画一图形,切换窗口,再切回来,发现图形上移,下面解决这个问题。
视口和窗口原点的改变
关于图形错位的说明
解决方法
在CGraphic2View::OnLButtonUp中编辑:
//CGraph graph(m_nDrawType,m_ptOrigin,point);//构造一个对象
//注:此时graph是一个局部变量,为它分配的内存在栈中,当这个函数结束时,对象也会被析构
//m_ptrArray.Add(&graph);//将graph对象保存到集合类的对象m_ptArray当中
OnPrepareDC(&dc);//调整图形设备显示
dc.DPtoLP(&m_ptOrigin);//将设备点转换成逻辑点
dc.DPtoLP(&point);//将设备点转换成逻辑点
CGraph *pGraph;//这也是一个局部的指针变量,内存在栈中
pGraph=new CGraph(m_nDrawType,m_ptOrigin,point);
//这一句调用它的构造函数为pGraph指针类型的变量在堆中分配一个内存空间,构造一个CGraph类的对象
//因为用new所分配的内存都是在堆中分配的,在堆中分配的对象的内存,如果不去显示的调用Delete去释放
//这个对象内存,那么这个对象的生命周期是和应用程序保持一致的
m_ptrArray.Add(pGraph);//将之前在堆中构造的CGraph类的对象地地址索引保存到集合类的对象m_ptArray当中
说明:OnPrepareDC会随时根据滚动窗口的位置来调整视口的原点。
5.保存图形和重绘图形的另两种方式
先在CGraphic2View类上添加一个私有的成员变量CMetaFileDC m_dcMetaFile; ,
然后在构造函数当中:
CGraphic2View::CGraphic2View()
{
m_nDrawType=0;
m_ptOrigin=0;
m_dcMetaFile.Create();//创建一个内存的CMetaFileDC对象
}
然后在CGraphic2View::OnLButtonUp函数中修改:
void CGraphic2View::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));
//创建透明画刷
//dc.SelectObject(pBrush);
m_dcMetaFile.SelectObject(pBrush);
switch(m_nDrawType)
{
case 1:
//dc.SetPixel(point,RGB(0,0,0));
m_dcMetaFile.SetPixel(point,RGB(0,0,0));//m_dcMetaFile元文件
//m_dcCompatible.SetPixel(point,RGB(0,0,0));//m_dcCompatible兼容DC
break;
case 2:
//dc.MoveTo(m_ptOrigin);
//dc.LineTo(point);
m_dcMetaFile.MoveTo(m_ptOrigin);
m_dcMetaFile.LineTo(point);
//m_dcCompatible.MoveTo(m_ptOrigin);
//m_dcCompatible.LineTo(point);
break;
case 3:
//dc.Rectangle(CRect(m_ptOrigin,point));
m_dcMetaFile.Rectangle(CRect(m_ptOrigin,point));
//m_dcCompatible.Rectangle(CRect(m_ptOrigin,point));
break;
case 4:
//dc.Ellipse(CRect(m_ptOrigin,point));
m_dcMetaFile.Ellipse(CRect(m_ptOrigin,point));
//m_dcCompatible.Ellipse(CRect(m_ptOrigin,point));
break;
}
CView::OnLButtonUp(nFlags, point);
}
并在CGraphic2View::OnDraw函数中修改:
void CGraphic2View::OnDraw(CDC* pDC)
{
CGraphic2Doc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
HMETAFILE hmetaFile;
hmetaFile=m_dcMetaFile.Close();//关闭元文件DC,并获得它的句柄
pDC->PlayMetaFile(hmetaFile);//播放元文件
m_dcMetaFile.Create();//再次创建一个元文件
m_dcMetaFile.PlayMetaFile(hmetaFile);//去播放先前的元文件,在元文件DC中绘制
DeleteMetaFile(hmetaFile);//删除元文件
}
运行,绘制图形,改变窗口大小后,图形显示出来了。
6.将绘制的元文件保存到元文件当中和打开保存的元文件
方式一:利用元文件
先给IDR_MAINFRAME菜单的保存和打开子项添加命令响应,编辑:
void CGraphic2View::OnFileSave()
{
//保存元文件
HMETAFILE hmetaFile;
hmetaFile=m_dcMetaFile.Close();
CopyMetaFile(hmetaFile,"meta.wmf");//拷贝元文件
m_dcMetaFile.Create();//重新创建元文件,以便下一次绘画
DeleteMetaFile(hmetaFile);//删除元文件
}
void CGraphic2View::OnFileOpen()
{
//打开元文件
HMETAFILE hmetaFile;
hmetaFile=GetMetaFile("meta.wmf");//得到元文件句柄
m_dcMetaFile.PlayMetaFile(hmetaFile);//播放元文件
DeleteMetaFile(hmetaFile);//删除元文件句柄
Invalidate();//引起窗口的重画
}
方式二:利用兼容DC
首先在CGraphic2View类当中增加一个私有成员变量 CDC m_dcCompatible; 然后在CGraphic2View::OnLButtonUp函数中修改:
void CGraphic2View::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));
//创建透明画刷
//dc.SelectObject(pBrush);
m_dcMetaFile.SelectObject(pBrush);
if(!m_dcCompatible.m_hDC)//判断兼容DC是否被创建
{
m_dcCompatible.CreateCompatibleDC(&dc);//创建一个与当前DC兼容的兼容DC
CRect rect;
GetClientRect(&rect);//获取客户区域大小
CBitmap bitmap;//创建一个bitmap对象
bitmap.CreateCompatibleBitmap(&dc,rect.Width(),rect.Height());//创建兼容位图
m_dcCompatible.SelectObject(&bitmap);//将位图选进兼容DC中
m_dcCompatible.BitBlt(0,0,rect.Width(),rect.Height(),&dc,0,0,SRCCOPY);//将源DC的设备表拷贝到兼容DC
m_dcCompatible.SelectObject(pBrush);//将透明的画刷选到兼容DC中
}
switch(m_nDrawType)
{
case 1:
//dc.SetPixel(point,RGB(0,0,0));
//m_dcMetaFile.SetPixel(point,RGB(0,0,0));//m_dcMetaFile元文件
m_dcCompatible.SetPixel(point,RGB(0,0,0));//m_dcCompatible兼容DC
break;
case 2:
//dc.MoveTo(m_ptOrigin);
//dc.LineTo(point);
//m_dcMetaFile.MoveTo(m_ptOrigin);
//m_dcMetaFile.LineTo(point);
m_dcCompatible.MoveTo(m_ptOrigin);
m_dcCompatible.LineTo(point);
break;
case 3:
//dc.Rectangle(CRect(m_ptOrigin,point));
//m_dcMetaFile.Rectangle(CRect(m_ptOrigin,point));
m_dcCompatible.Rectangle(CRect(m_ptOrigin,point));
break;
case 4:
//dc.Ellipse(CRect(m_ptOrigin,point));
//m_dcMetaFile.Ellipse(CRect(m_ptOrigin,point));
m_dcCompatible.Ellipse(CRect(m_ptOrigin,point));
break;
}
CView::OnLButtonUp(nFlags, point);
}
并在CGraphic2View::OnDraw函数中编辑:
void CGraphic2View::OnDraw(CDC* pDC)
{
CGraphic2Doc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
CRect rect;
GetClientRect(&rect);//得到客户的大小
pDC->BitBlt(0,0,rect.Width(),rect.Height(),&m_dcCompatible,0,0,SRCCOPY);
//将位图贴到客户区当中
}
转载出处:http://www.360doc.com/content/12/1112/15/9200790_247407525.shtml
示例下载地址:http://download.csdn.net/detail/qq_23992597/9436826