前面开发的应用中,如果调整窗口大小,会发现前面画线条,矩形等都不见,这是为什么呢?这里的原因是,当调整窗口大小时,窗口会收到WM_PAINT消息,即OS通过应用程序,需要对应用界面进行重绘。
使用元文件(画笔模式有闪烁异常)
Metafile是记录所有"绘图指令"的集合,拥有录制,重放的功能
•draw3view类中定义成员变量并在构造函数中初始化
// draw3view.h
CMetaFileDC m_dcMetaFile;
Cdraw3View::Cdraw3View() noexcept
{
// TODO: 在此处添加构造代码
...
m_dcMetaFile.Create();
}
•在画图的位置替换原有的DC
•在OnDraw函数中播放元文件,以实现重绘效果
void Cdraw3View::OnDraw(CDC* pDC)
{
Cdraw3Doc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
// TODO: 在此处为本机数据添加绘制代码
HMETAFILE metaFile;
// 结束录制,得到元文件
metaFile = m_dcMetaFile.Close();
// 在DC中播放元文件
pDC->PlayMetaFile(metaFile);
// 开始新的录制
m_dcMetaFile.Create();
// 在新的录制文件中重放前一个录制文件
m_dcMetaFile.PlayMetaFile(metaFile);
// 删除前一个录制文件
DeleteMetaFile(metaFile);
}
使用兼容设备描述表(画笔模式有闪烁异常)
兼容设备是一种内存设备,在显示图像的过程中,可以先在内存中准备这些图像,之后可以直接拷贝内存数据到真实的设备中,完成图像的显示。
•draw3view类中定义成员变量并在构造函数中初始化
// draw3view.h
CDC m_dcCompatible;
•初始化兼容设备表(二次缓冲)
if (!m_dcCompatible.m_hDC)
{
// 创建兼容DC
m_dcCompatible.CreateCompatibleDC(&dc);
CRect rect;
// 获得客户区大小
GetClientRect(&rect);
CBitmap bitmap;
// 创建兼容Bitmap,与客户区大小一致
bitmap.CreateCompatibleBitmap(&dc, rect.Width(), rect.Height());
// 兼容DC选入兼容bitmap
m_dcCompatible.SelectObject(&bitmap);
// 兼容DC填充指定颜色
m_dcCompatible.FillSolidRect(rect, RGB(255,255,255));
}
•画图位置替换原有的DC
•在OnDraw函数中复制兼容DC到真实DC上
void Cdraw3View::OnDraw(CDC* pDC)
{
Cdraw3Doc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
// TODO: 在此处为本机数据添加绘制代码
CRect rect;
// 获得客户区的大小
GetClientRect(&rect);
// 复制兼容DC的内容到真实DC上,实现绘制效果
pDC->BitBlt(0, 0, rect.Width(), rect.Height(), &m_dcCompatible, 0, 0, SRCCOPY);
}
使用自定义类存储绘图数据(所有功能均正常)
•仿元文件机制,记录绘图过程中关键信息
class Graph :
public CObject
{
public:
Graph(UINT drawType);
void AddPoint(CPoint point);
void AddPoint(CPoint start, CPoint end);
void Draw(CDC* pDC);
public:
// 形状
UINT m_nDrawType;
// 点的集合
CArray<CPoint> m_pPoints;
};
MFC定义的集合类
•模板集合类
CArray/CList/CMap
•非模板集合类
–数组
CObArray/CByteArray/CDWordArray/CPtrArray/CStringArray/CWordArray/CUIntArray
–链表
CObList/CPtrList/CStringList
–映射
使用Graph对象记录关键信息
在Draw3view.h添加成员变量
// 记录所有Graph对象的数组
CObArray m_pGraphs;
// 当前的画笔对象
Graph* m_TmpGraph;
在鼠标左键弹起的响应函数中处理画线,画矩形,画椭圆的信息
void Cdraw3View::OnLButtonUp(UINT nFlags, CPoint point)
{
...
if (m_DrawType != DT_PEN)
{
Graph *graph = new Graph(m_DrawType);
graph->AddPoint(m_pOrigin, point);
m_pGraphs.Add(graph);
}
else {
m_TmpGraph->AddPoint(point);
m_pGraphs.Add(m_TmpGraph);
}
...
}
在鼠标左键按下响应函数中添加对画笔的处理
void Cdraw3View::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
// 保存起点到成员变量
m_pOrigin = point;
if (m_DrawType == DT_PEN)
{
m_bDraw = TRUE;
m_TmpGraph = new Graph(DT_PEN);
m_TmpGraph->AddPoint(point);
}
CView::OnLButtonDown(nFlags, point);
}
在鼠标移动的响应函数中添加对画笔的处理
void Cdraw3View::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
if (m_DrawType == DT_PEN && m_bDraw == TRUE)
{
...
m_TmpGraph->AddPoint(point);
...
}
}
在OnDraw函数中实现重绘工作
void Cdraw3View::OnDraw(CDC* pDC)
{
// TODO: 在此处为本机数据添加绘制代码
for (int i = 0; i < m_pGraphs.GetSize(); i++)
{
Graph* gh = (Graph*)m_pGraphs.GetAt(i);
gh->Draw(pDC);
}
}
清理工作
Cdraw3View::~Cdraw3View()
{
int cnt = m_pGraphs.GetSize();
// 从数组末尾开始遍历,依次释放元素空间
while (cnt--)
{
delete m_pGraphs.GetAt(cnt);
}
// 清空数组
m_pGraphs.RemoveAll();
}
补充
要想记录画笔的所有信息
#pragma once
// Graph 命令目标
class Graph : public CObject
{
public:
Graph(UINT drawtype,UINT lineStyle,UINT lineWidth,COLORREF color);
//添加点到数组
void AddPoint(CPoint point);
void AddPoint(CPoint start,CPoint end);
void Draw(CDC* pDC);
virtual ~Graph();
public:
//绘制类型
UINT m_nDrawType;
UINT m_nLineStyle;
UINT m_nLineWidth;
COLORREF m_color;
//点的数组
CArray<CPoint> m_Points;
};
每次作画的时候更改画笔
```cpp
CPen pen(m_nLineStyle,m_nLineWidth,m_color);
CPen* pOldPen=pDC->SelectObject(&pen);
pDC->SelectObject(pOldPen);
MFC—用局部变量的引用导致程序崩溃的解决办法