首先,我们重新实现一下上次课实现的功能:通过菜单,选择画哪种图形,然后进行绘图。但是这个功能有一个严重的问题,就是窗口发生重绘(比如我们把程序最小化以后再重新显示)以后,原来的图就不见了!怎么解决这个问题呢?我们必须保存每次画的图。然后在重绘函数中把它们重新画出来。准确的说,其实我们只需要保存3样东西就行了:类型(画点、画线、画矩形、画椭圆)、起点、终点。我们可以用一个类封装它们。
class CGraph
{
public:
CGraph();
CGraph(UINT m_nDrawType, CPoint m_ptOrigin, CPoint m_ptEnd);
virtual ~CGraph();
UINT m_nDrawType;
CPoint m_ptOrigin;
CPoint m_ptEnd;
};
并且重载了构造函数:
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;
}
那么把它们存在什么地方呢?肯定不能使用数组,因为我们并不知道要存多少元素。MFC为我们封装了一个称为CPtrArray类,它支持void*类型的。通过GetAt来获得某个元素,通过Add 来增加一个元素,通过GetSize 来获得总的元素个数。我们为自己的view类增加一个CPtrArray类型的成员变量m_ptrArray,然后在每次OnLButtonUp消息的中,将前面提到的3个变量保存:
CGraph graph(m_nDrawType,m_ptOrigin,point);
m_ptrArray.Add(&graph);
然后,在OnDraw函数中把它们重新画出来:
void CCH_11_GRAPHView::OnDraw(CDC* pDC)
{
CCH_11_GRAPHDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
CBrush *pBrush = CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
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;
}
}
}
但是我们发现,当窗口重绘以后,原来的图像依然没有显示出来,问题出在了哪里呢?其实在于OnLButtonUp中的构造的graph是一个局部变量,所以,当程序运行完以后,它就被析构了。即使我们保存了它的地址,但是在OnDraw函数中使用的,实际上是已经被虚构的无效的地址了,所以不能正常绘图。解决的办法也很简单,就是在OnLButtonUp中new一个graph出来,然后增加到m_ptrArray中就行了:
// CGraph graph(m_nDrawType,m_ptOrigin,point);
// m_ptrArray.Add(&graph);
CGraph *pGraph = new CGraph(m_nDrawType,m_ptOrigin,point);
m_ptrArray.Add(pGraph);