14.4.3 使用鼠标绘图(11)
如果在类定义中手动添加这个声明,就需要在.cpp文件中添加该函数的完整定义,如下所示:
- // Create an element of the current type
- CElement* CSketcherView::CreateElement(void) const
- {
- // Get a pointer to the document for this view
- CSketcherDoc* pDoc = GetDocument();
- ASSERT_VALID(pDoc); // Verify the pointer is good
- // Get the current element color
- COLORREF color = static_cast<COLORREF>(pDoc->GetElementColor());
- // Now select the element using the type stored in the document
- switch(pDoc->GetElementType())
- {
- case ElementType::RECTANGLE:
- return new CRectangle(m_FirstPoint, m_SecondPoint, color);
- case ElementType::CIRCLE:
- return new CCircle(m_FirstPoint, m_SecondPoint, color);
- case ElementType::CURVE:
- return new CCurve(m_FirstPoint, m_SecondPoint, color);
- case ElementType::LINE:
- return new CLine(m_FirstPoint, m_SecondPoint, color);
- default:
- // Something’s gone wrong
- AfxMessageBox(_T(“Bad Element code”), MB_OK);
- AfxAbort();
- return nullptr;
- }
- }
和前面看到的一样,首先调用GetDocument()获取指向文档的指针。为了安全起见,使用ASSERT_ VALID()宏确保返回一个有效的指针。在应用程序的调试版中,这个宏将调用对象的AssertValid()成员,它被指定为这个宏的参数。这将检查当前对象的有效性,如果指针为NULL,或者这个对象在某个方面有缺陷,那么将显示一条错误消息。在应用程序的发布版本中,ASSERT_ VALID()宏没有任何作用。
在创建元素时,GetElementColor()函数把ElementColor枚举返回为一个颜色值,但不能使用ElementColor枚举,因为它的类型是错误的。但是,每个枚举的数值是一个COLORVALUE颜色值,所以可以把该枚举转换为COLORREF类型,来获取颜色值。switch语句基于GetElementType()返回的类型,选择要创建的元素。在类型不是ElementType枚举的情况下(不太可能),应显示一个消息框,并结束程序。在SketcherView.cpp中应给四个派生元素类的头文件添加#include指令。
处理WM_LBUTTONUP消息
WM_LBUTTONUP消息完成创建元素的过程。这种消息处理程序的工作是把创建的元素的最终版本传递到文档对象,然后清理视图对象数据成员。可以在CSketcherDoc类中添加一个STL容器对象来存储元素。还需要一个文档类函数,在需要添加元素时调用。我们还可以在某处从文档中删除元素。此时使用vector<T>会比较快,但这里使用列表容器,因为后面要使用矢量容器来存储曲线的点。给CSketcherDoc添加protected数据成员:
- std::list<std::shared_ptr<CElement>> m_Sketch; // A list containing the sketch
sketch元素在堆上创建,所以可以存储指向元素的智能指针,而不是存储元素本身。在SketcherDoc.h中给memory和Element.h头文件添加#include指令。销毁文档对象时,也会销毁m_Sketch和它包含的智能指针指向的所有元素。
在CSketcherDoc类的Implementation部分把下面的函数定义添加为public成员:
- // Add a sketch element
- void AddElement(std::shared_ptr<CElement>& pElement)
- {
- m_Sketch.push_back(pElement);
- }
- // Delete a sketch element
- void DeleteElement(std::shared_ptr<CElement>& pElement)
- {
- m_Sketch.remove(pElement);
- }
第一个函数把传递为参数、指向元素的智能指针添加到m_Sketch列表中。第二个函数从列表中删除指针,这也会删除堆中的元素。下一章将实现删除sketch元素的UI机制。
现在可以实现OnLButtonUp()处理程序了。在SketcherView.cpp的函数定义中添加如下代码:
- void CSketcherView::OnLButtonUp(UINT nFlags, CPoint point)
- {
- // Make sure there is an element
- if(m_pTempElement)
- {
- // Add the element pointer to the sketch
- GetDocument()->AddElement(m_pTempElement);
- InvalidateRect(&m_pTempElement->GetEnclosingRect());
- m_pTempElement.reset(); // Reset the element pointer
- }
- }
在处理m_pTempElement之前,if语句将验证它不为nullptr。用户始终可以在不移动鼠标的情况下按下和释放鼠标左键,这时不创建任何元素。只要有一个元素,指向该元素的指针就传递给文档对象函数,将它添加到草图中。最后,m_pTempElement指针重置为nullptr,等待下一次用户绘制元素。