void draw(CDC* pDC);//绘制图元 int GetType();//返回图元类型 具体的实现代码如下: void CLine::draw(CDC *pDC) {
//绘制直线段
pDC->MoveTo(GetStartPoint()); pDC->LineTo(GetEndPoint()); }
int CLine::GetType() {
//返回图元类型为直线段 return 1; }
2.3.3 椭圆图元子类CEllipse
我们定义椭圆图元子类的类名为CEllipse,其基类为CMapElement。同样该子类需要添加如下公有成员函数:
public:
void draw(CDC* pDC);//绘制图元 int GetType();//返回图元类型 具体的实现代码如下:
void CEllipse::draw(CDC *pDC) {
//获得椭圆的控制点
CPoint sp = GetStartPoint(); CPoint ep = GetEndPoint(); //绘制椭圆边界线
pDC->Arc(sp.x,sp.y,ep.x,ep.y,sp.x,sp.y,ep.x,ep.y); pDC->Arc(sp.x,sp.y,ep.x,ep.y,ep.x,ep.y,sp.x,sp.y); }
int CEllipse::GetType() {
//返回图元类型为椭圆 return 2; }
draw函数中绘制椭圆边界线的方法和前面我们介绍的在DrawLButtonUp函数中绘制椭圆边界线的方法相同。 2.3.4 椭圆区域图元子类CEllipseRegion
我们定义椭圆区域图元子类的类名为CEllipseRegion,其基类为CMapElement。同样需要添加如下公有成员函数:
public:
void draw(CDC* pDC);//绘制图元 int GetType();//返回图元类型 具体的实现代码如下:
void CEllipseRegion::draw(CDC *pDC) {
//获得椭圆区域的控制点 CPoint sp = GetStartPoint(); CPoint ep = GetEndPoint(); //绘制椭圆区域
pDC->Ellipse(sp.x,sp.y,ep.x,ep.y); }
int CEllipseRegion::GetType() {
//返回图元类型为椭圆区域 return 3; }
2.3.5 矩形区域图元子类CRectangleRegion
我们定义矩形区域图元子类的类名为CRectangleRegion,其基类为CMapElement。添加如下公有成员函数:
public:
void draw(CDC* pDC);//绘制图元 int GetType();//返回图元类型 具体的实现代码如下:
void CRectangleRegion::draw(CDC *pDC) {
//获得矩形区域控制点 CPoint sp = GetStartPoint(); CPoint ep = GetEndPoint(); //绘制矩形区域
pDC->Rectangle(sp.x,sp.y,ep.x,ep.y); }
int CRectangleRegion::GetType() {
//返回图元类型为矩形区域 return 4; } 2.3.6 图元重画
现在我们已经定义了图元基类和对应不同图形的图元子类,在用户绘制完一个图形后,我们可以实例化一个对应的图元子类对象,该对象实例就对应了用户所画的图形。在重画图元时,只需调用该对象实例的draw成员函数即可。现在我们需要一个存放这些对象实例的地方。
因为我们定义的图元基类CMapElement的基类是CObject。MFC提供了存放CObject对象实例指针的列表对象类CObArray。列表类实现了对类对象的按顺序存取,可以像对象数组一样通过指定类对象在列表中的序号来访问类对象。列表类优于数组的地方在于不用先声明大小以申请空间,它可以动态的改变列表的大小。下面简单介绍一下CObArray中常用的成员函数。
? Add函数,用于向列表中添加CObject对象指针,其函数声明如下: int Add(CObject* newElement) throw(CMemoryException);
参数newElement为要添加的CObject对象指针。如果添加成功,函数返回添加的对象指针在列表中的序号。新添加的对象添加到列表的尾部,并且列表的序号从0开始,即如果当前列表中已经有4个对象指针,则添加了新的对象指针后,函数返回4。如果添加失败,函数抛出CMemoryException异常。
? GetAt函数,用于获得指定序号的列表中的CObject对象指针,其函数声明如下:
CObject* GetAt(int nIndex) const;
参数nIndex指定了要获得对象指针在列表中的序号,列表序号从0开始,即如果想获得列表中的第5个对象指针,需要传入参数值4。函数返回CObject对象指针。传入的序号要确保是列表的有效序号,假设列表中当前有5个对象指针,如果传入的参数值大于4将导致错误。
? GetSize函数,用于获得当前列表的大小,即存放的CObject对象指针的数量,其函数声明如下:
int GetSize() const;
? SetAt函数,用于将指定序号的列表中的CObject对象指针替换为传入的CObject对象指针,要确保指定的序号是有效的,其函数声明如下:
void SetAt(int nIndex, CObject* newElement);
? InsertAt函数,用于在指定序号的位置插入传入的CObject对象指针或CObArray,其函数声明如下:
void InsertAt(int nIndex, CObject* newElement, int nCount = 1) throw(CMemoryException);
void InsertAt(int nStartIndex, CObArray* pNewArray) throw(CMemoryException);
第一个函数的参数nIndex指定了要插入的位置序号,该序号可以比列表的实际大小要大;参数newElement为要插入的CObject对象指针;参数nCount指定了传入的对象指针要插入多少次,默认值为1。第二个函数的参数nIndex与第一个函数中的含义相同,也可以大于列表的实际大小;参数pNewArray为指向一个CObArray列表的指针。该函数将指定列表中的所有CObject对象指针插入到当前列表中的指定位置。如果函数执行失败,将抛出CMemoryException异常。
? RemoveAt函数,用于移除列表中指定序号的CObject对象指针,其函数声明如下:
void RemoveAt(int nIndex, int nCount = 1);
参数nIndex指定了开始移除的位置序号,该序号要确保有效;参数nCount指定了要移除的CObject对象指针的数量,默认为1。如果指定移除的数量多于从指定的移除位置开始的列表中实际的CObject对象指针的数量,则函数将把从nIndex开始的列表中所有的CObject对象指针移除。这里需要注意的是移除CObject对象指针只是从列表中移除,而实际的CObject对象仍然存在,只是不
能再通过列表访问到。
? RemoveAll函数,移除列表中所有的CObject对象指针,其函数声明如下: void RemoveAll();
以上是CObArray中比较常用的成员函数。在MFC中还有很多列表类,其主要差别是存放的对象不同,但是基本上都提供了以上功能的成员函数,只是参数类型会有所不同。
在我们的绘图应用程序中不直接使用CObArray类,而是创建一个新类CMapList,该类从CObArray类继承。这样如果我们需要,可以添加成员函数或覆盖已有的成员函数来满足我们的要求。在项目中添加新类CMapList,其基类为CObArray。同创建CMapElement类时一样,我们需要在CMapList类的头文件中包含afxtempl.h头文件。编写CMapList类的析构函数,输入如下代码:
CMapList::~CMapList() {
//销毁列表中所有指针所指向的对象 for (int i=0;i 编写此段代码是为了避免造成内存泄漏。 我们在CDrawMapDoc类的头文件中包含CMapList的头文件MapList.h,然后在CDrawMapDoc类中添加下面的成员变量: CMapList m_MapList;//当前绘制的图元的列表 图元列表放在CDrawMapDoc类中是因为在文档视图体系中文档用于存储数据。现在我们需要在用户绘制完图元时,实例化对应的图元子类,设置控制点,然后调用draw函数绘制图元,最后将图元子类的指针存入m_MapList列表中。修改DrawLButtonUp成员函数,输入如下代码: //鼠标绘图时鼠标左键抬起处理函数 void CDrawMapView::DrawLButtonUp(UINT nFlags, CPoint point) { SetCursor(m_Cursor);//设置使用光标资源 ReleaseCapture();//释放鼠标 CDC* pDC = this->GetDC();//获得设备环境对象 //获得文档指针 CDrawMapDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); //绘制的是直线段 if (m_DrawType == 1) { //构造直线段图元对象指针 CLine* line = new CLine(); //设置控制点 line->SetStartPoint(m_StartPoint); line->SetEndPoint(m_EndPoint); //绘制直线段图元 line->draw(pDC); //添加直线段图元对象指针到图元列表中 pDoc->m_MapList.Add(line); } //绘制的是椭圆 if (m_DrawType == 2) { //构造椭圆图元对象指针 CEllipse* ellipse = new CEllipse(); //设置控制点 ellipse->SetStartPoint(m_StartPoint); ellipse->SetEndPoint(m_EndPoint); //绘制椭圆图元 ellipse->draw(pDC); //添加椭圆图元对象指针到图元列表中 pDoc->m_MapList.Add(ellipse); } //绘制的是椭圆区域 if (m_DrawType == 3) { //构造椭圆区域对象指针 CEllipseRegion* ellipseRegion = new CEllipseRegion(); //设置控制点 ellipseRegion->SetStartPoint(m_StartPoint); ellipseRegion->SetEndPoint(m_EndPoint); //绘制椭圆区域图元 ellipseRegion->draw(pDC); //添加椭圆区域图元对象指针到图元列表中 pDoc->m_MapList.Add(ellipseRegion); } //绘制的是矩形区域 if (m_DrawType == 4) { int c; //确保m_StartPoint确实为矩形区域的左上角 //m_EndPoint确实是矩形区域的右下角 if (m_StartPoint.x > m_EndPoint.x) { c = m_StartPoint.x; m_StartPoint.x = m_EndPoint.x;