Windows使用图形设备界面(Graphical Device Interface,GDI)定义输出。GDI支持在对图形输出编程时不依赖显示它的硬件,这意味着程序不进行任何修改,就可以在具有不同显示硬件的不同机器上运行。
设备上下文
在输出设备上进行绘图操作时,必须使用设备上下文。
设备上下文是一种Windows数据结构,它包含的信息允许Windows将输出请求转换成物理输出设备上的动作,输出请求采用与设备无关的GDI函数调用形式。
MFC类CDC封装了一个设备上下文,所以对该类型的对象调用函数,就可以执行所有的绘图操作。
设备上下文提供了一种称为映射模式的可选坐标系统,它将被自动转换成客户区坐标。
映射模式
设备上下文中的每种映射模式都由一个ID标志,其方式与标志Windows消息类似,每个ID都由前缀MM_,表明它定义了映射模式。
MFC Application Wizard生成的类:
void CSketcherView::OnDraw(CDC* /*pDC*/)
{
CSketcherDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
// TODO: 在此处为本机数据添加绘制代码
}
定义设备上下文对象的类。
MFC包括了一些派生于CDC的更专用的类。例如,CClientDC类优于CDC类的地方是它始终包含只代表窗口工作区的设备上下文。
CDC::MoveTo
将当前位置移动到 (x
y
或 point
) 指定的点。
CPoint MoveTo(
int x,
int y);
CPoint MoveTo(POINT point);
x |
指定新位置的逻辑 x 坐标。 |
y |
指定新位置的逻辑 y 坐标。 |
point |
指定新位置。 可以传递 POINT 此参数的结构或 CPoint 对象。 |
返回值
作为对象的上一 CPoint
位置的 x 坐标和 y 坐标。
CDC::LineTo
从当前位置绘制一条线,但不包括由 x
y
(或 point
) 指定的点。
BOOL LineTo(
int x,
int y);
BOOL LineTo(POINT point);
x |
指定新位置的逻辑 x 坐标。 |
y |
指定新位置的逻辑 y 坐标。 |
point |
指定新位置。 可以传递 POINT 此参数的结构或 CPoint 对象。 |
返回值
如果绘制线条,则为非零;否则为 0。
CDC::Ellipse
绘制椭圆形。
BOOL Ellipse(
int x1,
int y1,
int x2,
int y2);
BOOL Ellipse(LPCRECT lpRect);
x1 |
指定椭圆边界矩形左上角的逻辑 x 坐标。 |
y1 |
指定椭圆边界矩形左上角的逻辑 y 坐标。 |
x2 |
指定椭圆边界矩形右下角的逻辑 x 坐标。 |
y2 |
指定椭圆边界矩形右下角的逻辑 y 坐标。 |
lpRect |
指定椭圆的边界矩形。 还可以传递 CRect 此参数的对象。 |
返回值
如果该函数成功,则为非 0;否则为 0。
CDC::Arc
绘制椭圆弧线。
BOOL Arc(
int x1,
int y1,
int x2,
int y2,
int x3,
int y3,
int x4,
int y4);
BOOL Arc(
LPCRECT lpRect,
POINT ptStart,
POINT ptEnd);
x1 |
指定边界矩形左上角的 x 坐标 (逻辑单元) |
y1 |
指定边界矩形左上角的 y 坐标 (逻辑单元) 。 |
x2 |
指定边界矩形右下角的 x 坐标 (逻辑单元) 。 |
y2 |
指定边界矩形右下角的 y 坐标 (逻辑单元 )。 |
x3 |
指定在(逻辑单元) 中定义弧线起点 的点的 x 坐标。 这一点不必完全躺在弧线上。 |
y3 |
指定在(逻辑单元) 中定义弧的起点 的点的 y 坐标。 这一点不必完全躺在弧线上。 |
x4 |
指定点的 x 坐标,该点在(逻辑单元) 中定义弧的终结点 。 这一点不必完全躺在弧线上。 |
y4 |
指定在(逻辑单元) 中定义弧的终结点 的点的 y 坐标。 这一点不必完全躺在弧线上。 |
lpRect |
指定(逻辑单元) 中的边界矩形 。 可以为此参数传递一个 LPRECT 或一个 CRect 对象。 |
ptStart |
指定点的 x 坐标和 y 坐标,该点在(逻辑单元) 中定义弧的起点 。 这一点不必完全躺在弧线上。可以传递 POINT 此参数的结构或 CPoint 对象。 |
ptEnd |
指定点的 x 坐标和 y 坐标,该点在 (逻辑单元) 中定义弧线的终点。 这一点不必完全躺在弧线上。可以传递 POINT 此参数的结构或 CPoint 对象。 |
返回值
如果该函数成功,则为非 0;否则为 0。
CDC::SelectObject
选择设备上下文中的对象。
CPen* SelectObject(CPen* pPen);
CBrush* SelectObject(CBrush* pBrush);
virtual CFont* SelectObject(CFont* pFont);
CBitmap* SelectObject(CBitmap* pBitmap);
int SelectObject(CRgn* pRgn);
CGdiObject* SelectObject(CGdiObject* pObject);
pPen |
指向要选择的对象的 CPen 指针 |
pBrush |
指向要选择的对象的 CBrush 指针。 |
pFont |
指向要选择的对象的 CFont 指针。 |
pBitmap |
指向要选择的对象的 CBitmap 指针。 |
pRgn |
指向要选择的对象的 CRgn 指针。 |
pObject |
指向要选择的对象的 CGdiObject 指针。 |
返回值
指向要替换的对象的指针。 这是指向派生自 CGdiObject
其中一个类的对象(例如 CPen
,具体取决于使用哪个版本的函数)的指针。 如果出现错误,则返回值 NULL
。 此函数可能会返回指向临时对象的指针。 此临时对象仅在处理一条Windows消息期间有效。 有关详细信息,请参阅 CGdiObject::FromHandle
。
采用区域参数的成员函数的版本执行与成员函数相同的任务 SelectClipRgn
。 其返回值可以是下列任一值:
COMPLEXREGION
新的剪辑区域具有重叠的边框。
ERROR
设备上下文或区域无效。
NULLREGION
新的剪辑区域为空。
SIMPLEREGION
新的剪辑区域没有重叠的边框。
Cpen类
CPen 类 | Microsoft Docs
封装一个 Windows 图形设备接口 (GDI) 笔。
CPen();
CPen(
int nPenStyle,
int nWidth,
COLORREF crColor);
CPen(
int nPenStyle,
int nWidth,
const LOGBRUSH* pLogBrush,
int nStyleCount = 0,
const DWORD* lpStyle = NULL);
PenStyle |
指定笔样式。 此构造函数的第一个版本中的此参数可以是下列值之一:
构造函数的第二个版本
构造函数的第二个版本
结尾端可以是下列值之一:
联接可以是以下值之一:
|
|||||||||||||||||
nWidth |
指定笔的宽度。
|
|||||||||||||||||
crColor |
包含用于笔的 RGB 颜色。 | |||||||||||||||||
pLogBrush |
LOGBRUSH 指向结构。 如果 nPenStyle 为 PS_COSMETIC , lbColor 则结构的成员 LOGBRUSH 指定笔的颜色,并且 lbStyle 结构的成员 LOGBRUSH 必须设置为 BS_SOLID 。 如果 nPenStyle 为 PS_GEOMETRIC ,则必须将所有成员用于指定笔的画笔属性。 |
|||||||||||||||||
nStyleCount |
指定数组的长度(以双字单位表示) lpStyle 。 如果 nPenStyle 不 PS_USERSTYLE 是,该值必须为零。 |
|||||||||||||||||
lpStyle |
指向一组双字值。 第一个值指定用户定义样式中第一个短划线的长度,第二个值指定第一个空间的长度,依此类推。 如果 nPenStyle 不 PS_USERSTYLE 是,则该指针必须是 NULL 。 |
使用指定的样式、宽度和画笔特性创建逻辑修饰或几何钢笔,并将其附加到 CPen
对象。
BOOL CreatePen(
int nPenStyle,
int nWidth,
COLORREF crColor);
BOOL CreatePen(
int nPenStyle,
int nWidth,
const LOGBRUSH* pLogBrush,
int nStyleCount = 0,
const DWORD* lpStyle = NULL);
nPenStyle |
指定笔的样式。 有关可能值的列表,请参见构造函数中 CPen 的 nPenStyle 参数。 |
nWidth |
指定笔的宽度。
|
crColor |
包含用于笔的 RGB 颜色。 |
pLogBrush |
LOGBRUSH指向结构。 如果 nPenStyle 为 PS_COSMETIC , lbColor 则结构的成员 LOGBRUSH 指定笔的颜色,并且 lbStyle 结构的成员 LOGBRUSH 必须设置为 BS_SOLID 。 如果 nPenStyle 为 PS_GEOMETRIC ,则必须将所有成员用于指定笔的画笔属性。 |
nStyleCount |
指定数组的长度(以双字单位表示) lpStyle 。 如果 nPenStyle 不 PS_USERSTYLE 是,该值必须为零。 |
lpStyle |
指向一组双字值。 第一个值指定用户定义样式中第一个短划线的长度,第二个值指定第一个空间的长度,依此类推。 如果 nPenStyle 不 PS_USERSTYLE 是,则该指针必须是 NULL 。 |
返回值
如果成功,则为非零; 如果方法失败,则为零。
CBrush
封装一个 Windows 图形设备接口 (GDI) 画笔。
class CBrush : public CGdiObject
CBrush::CBrush
构造 CBrush
对象。
CBrush();
CBrush(COLORREF crColor);
CBrush(int nIndex, COLORREF crColor);
explicit CBrush(CBitmap* pBitmap);
crColor |
指定画笔的前景色作为 RGB 颜色。 如果画笔为影线,则此参数指定阴影的颜色。 |
nIndex |
指定画笔的阴影样式。 它可以是下列值之一:
|
pBitmap |
指向一个 CBitmap 对象,该对象指定画笔用于绘制的位图。 |
CBrush::CreateHatchBrush
使用指定的阴影模式和颜色初始化画笔。
BOOL CreateHatchBrush(
int nIndex,
COLORREF crColor);
crColor |
指定画笔的前景色作为 RGB 颜色 (阴影) 的颜色。 有关详细信息,请参阅 COLORREF 中的 Windows SDK。 |
nIndex |
指定画笔的阴影样式。 它可以是下列值之一:
|
返回值
如果成功,则不为 0;否则为 0。
.cpp自动添加
void CSketcherView::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
CView::OnLButtonUp(nFlags, point);
}
void CSketcherView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
CView::OnLButtonDown(nFlags, point);
}
void CSketcherView::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
CView::OnMouseMove(nFlags, point);
}
.h自动添加
public:
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
当用户释放鼠标左键时,框架将调用此成员函数。
afx_msg void OnLButtonUp(
UINT nFlags,
CPoint point);
nFlags |
指示各种虚拟密钥是否已关闭。 此参数可以是以下值的任意组合:
|
point |
指定游标的 x 坐标和 y 坐标。 这些坐标始终相对于窗口左上角。 |
用鼠标绘图
InvalidateRect
InvalidateRect函数将一个矩形添加到指定窗口的更新区域。更新区域表示必须重绘的窗口客户区部分。
BOOL InvalidateRect(
[in] HWND hWnd,
[in] const RECT *lpRect,
[in] BOOL bErase
);
[in] hWnd | 更新区域已更改的窗口的句柄。如果此参数为NULL,系统将无效并重绘所有窗口,而不仅仅是此应用程序的窗口,并在函数返回之前发送WM_ERASEBKGND和WM_NCPAINT消息。不建议将此参数设置为NULL 。 |
[in] lpRect | 指向RECT结构的指针,该结构包含要添加到更新区域的矩形的客户端坐标。如果此参数为NULL,则将整个客户区添加到更新区域。 |
[in] bErase | 指定在处理更新区域时是否擦除更新区域内的背景。如果此参数为TRUE ,则在调用BeginPaint函数时将擦除背景。如果此参数为FALSE,则背景保持不变。 |
返回值
如果函数成功,则返回值非零。如果函数失败,则返回值为零。
//SketcherView.h
class CSketcherView : public CView
{
protected:
CPoint m_FirstPoint;
}
//SketcherView.cpp
CSketcherView::CSketcherView(): m_FirstPoint{ CPoint{} }
{
// TODO: 在此处添加构造代码
}
定义元素的类
生成 CElement.h和CElement.cpp
//Element.h
#pragma once
// CElement 命令目标
class CElement : public CObject
{
public:
CElement();
virtual ~CElement();
};
// Element.cpp: 实现文件
//
#include "pch.h"
#include "Sketcher.h"
#include "CElement.h"
// CElement
CElement::CElement()
{
}
CElement::~CElement()
{
}
其他元素类把CElement而不是MFC类作为基类,所以应该把类类别选作C++。
自动生产CLine.h和CLine.cpp
//Line.h
#pragma once
#include "Element.h"
class CLine :
public CElement
{
};
//Line.cpp
#include "pch.h"
#include "Line.h"
相同步骤添加CRectangle、CCircle、CCurve,以CElement为基类。
// SketcherView.h: CSketcherView 类的接口
//
#pragma once
#include
#include "Element.h"
CElement类
//Element.h
#pragma once
// CElement 命令目标
class CElement : public CObject
{
public:
virtual ~CElement();
virtual void Draw(CDC* pDC) {} // Virtual draw operation
const CRect& GetEnclosingRect() const { return m_EnclosingRect; }// Get the element enclosing rectangle
protected:
CPoint m_StartPoint; // Element position
int m_PenWidth; // Pen width
COLORREF m_Color; // Color of an element
CRect m_EnclosingRect; // Rectangle enclosing an element
protected:
void CreatePen(CPen& aPen);
// Constructors protected so they cannot be called outside the class
CElement();
CElement(const CPoint& start, COLORREF color, int penWidth = 1);
};
// Element.cpp: 实现文件
#include "pch.h"
#include "Sketcher.h"
#include "Element.h"
void CElement::CreatePen(CPen& aPen)
{
if (!aPen.CreatePen(PS_SOLID, m_PenWidth, m_Color))
{
//创建笔失败
AfxMessageBox(_T("Pen创建失败."), MB_OK);
AfxAbort();
}
}
// CElement
CElement::CElement()
{
}
CElement::~CElement()
{
}
// CElement 成员函数
CElement::CElement(const CPoint& start, COLORREF color, int penWidth) : m_StartPoint{ start }, m_PenWidth{ penWidth }, m_Color{ color }
{
}
CLine类
//CLine.h
#pragma once
#include "Element.h"
class CLine : public CElement
{
public:
virtual ~CLine();
virtual void Draw(CDC* pDC) override;
CLine(const CPoint& start, const CPoint& end, COLORREF color);
protected:
CPoint m_EndPoint;
protected:
CLine();
};
// CLine.cpp: 实现文件
#include "pch.h"
#include "Line.h"
CLine::~CLine()
{
}
void CLine::Draw(CDC* pDC)
{
CPen aPen;
CreatePen(aPen);// 基类CElement::CreatePen(CPen& aPen)
CPen* pOldPen{ pDC->SelectObject(&aPen) }; //SelectObject:指向要替换的对象的指针(旧画笔)。
pDC->MoveTo(m_StartPoint); //画直线
pDC->LineTo(m_EndPoint);
pDC->SelectObject(pOldPen); //恢复旧画笔
}
CLine::CLine(const CPoint& start, const CPoint& end, COLORREF color) :
CElement{ start,color }, m_EndPoint{end}
{
//定义封闭的矩形
m_EnclosingRect = CRect{ start,end };
m_EnclosingRect.NormalizeRect(); //规范化 CRect ,使高度和宽度都为正。
m_EnclosingRect.InflateRect(m_PenWidth, m_PenWidth); // 增加CRect的宽度和高度 。
}
CLine::CLine()
{
}
CRectangle类
//Rectangle.h
#pragma once
#include "Element.h"
class CRectangle :
public CElement
{
public:
virtual ~CRectangle();
virtual void Draw(CDC* pDC) override;
CRectangle(const CPoint& start, const CPoint& end, COLORREF color);
protected:
CPoint m_BottomRight; //右下角坐标点
protected:
CRectangle();
};
//Rectangle.cpp
#include "pch.h"
#include "Rectangle.h"
#include
CRectangle::~CRectangle()
{
}
void CRectangle::Draw(CDC* pDC)
{
CPen aPen;
CreatePen(aPen);// 基类CElement::CreatePen(CPen& aPen)
CPen* pOldPen{ pDC->SelectObject(&aPen) }; //SelectObject:指向要替换的对象的指针(旧画笔)。
CBrush* pOldBrush{ dynamic_cast (pDC->SelectStockObject(NULL_BRUSH)) }; //选择一个 CGdiObject 对象,该对象对应于预定义的股票笔、画笔或字体之一。 NULL_PEN Null 笔
pDC->Rectangle(m_StartPoint.x, m_StartPoint.y, m_BottomRight.x, m_BottomRight.y); //画矩形
pDC->SelectObject(pOldBrush); //恢复画刷
pDC->SelectObject(pOldPen); //恢复旧画笔
}
CRectangle::CRectangle(const CPoint& start, const CPoint& end, COLORREF color) : CElement{start,color}
{
m_StartPoint = CPoint{ (std::min)(start.x,end.x),(std::min)(start.y,end.y) }; //左上角取x,y的最小值
m_BottomRight = CPoint{ (std::max)(start.x,end.x),(std::max)(start.y,end.y) }; //右下角取x,y的最大值
if ((m_BottomRight.x - m_StartPoint.x) < 2) m_BottomRight.x = m_StartPoint.x + 2;
if ((m_BottomRight.y - m_StartPoint.y) < 2) m_BottomRight.y = m_StartPoint.y + 2;
m_EnclosingRect = CRect{ m_StartPoint, m_BottomRight };
m_EnclosingRect.InflateRect(m_PenWidth, m_PenWidth);// 增加CRect的宽度和高度 。
}
CRectangle::CRectangle()
{
}
Circle
//Circle.h
#pragma once
#include "Element.h"
class CCircle :
public CElement
{
public:
virtual ~CCircle();
virtual void Draw(CDC* pDC) override;
CCircle(const CPoint& start, const CPoint& end, COLORREF color);
protected:
CPoint m_BottomRight;
protected:
CCircle();
};
//Circle.cpp
#include "pch.h"
#include "Circle.h"
#include
CCircle::~CCircle()
{
}
void CCircle::Draw(CDC* pDC)
{
CPen aPen;
CreatePen(aPen);// 基类CElement::CreatePen(CPen& aPen)
CPen* pOldPen{ pDC->SelectObject(&aPen) }; //SelectObject:指向要替换的对象的指针(旧画笔)。
CBrush* pOldBrush{ dynamic_cast (pDC->SelectStockObject(NULL_BRUSH)) }; //选择一个 CGdiObject 对象,该对象对应于预定义的股票笔、画笔或字体之一。 NULL_PEN Null 笔
pDC->Ellipse(m_StartPoint.x, m_StartPoint.y, m_BottomRight.x, m_BottomRight.y); //画矩形
pDC->SelectObject(pOldBrush); //恢复画刷
pDC->SelectObject(pOldPen); //恢复旧画笔
}
CCircle::CCircle(const CPoint& start, const CPoint& end, COLORREF color) : CElement{start , color}
{
long radius{static_cast(sqrt(static_cast(pow(end.x-start.x,2)+pow(end.y-start.y,2))))};
if (radius < 1L) radius = 1L;
m_StartPoint = CPoint{start.x - radius,start.y-radius};
m_BottomRight = CPoint{ start.x + radius,start.y + radius };
m_EnclosingRect = CRect{ m_StartPoint.x,m_StartPoint.y,m_BottomRight.x,m_BottomRight.y };
m_EnclosingRect.InflateRect(m_PenWidth, m_PenWidth); // 增加CRect的宽度和高度 。
}
CCircle::CCircle()
{
}
CCurve类
//Curve.h
#pragma once
#include "Element.h"
#include
class CCurve : public CElement
{
public:
virtual ~CCurve();
virtual void Draw(CDC* pDC) override;
CCurve(const CPoint& start, const CPoint& second, COLORREF color);
void AddSegment(const CPoint& point); // 向曲线添加段
protected:
std::vector m_Points; // 定义曲线的点群
protected:
CCurve();
};
//Curve.cpp
#include "pch.h"
#include "Curve.h"
#include
CCurve::~CCurve()
{
}
void CCurve::Draw(CDC* pDC)
{
CPen aPen;
CreatePen(aPen);// 基类CElement::CreatePen(CPen& aPen)
CPen* pOldPen{ pDC->SelectObject(&aPen) }; //SelectObject:指向要替换的对象的指针(旧画笔)。
pDC->MoveTo(m_StartPoint); //画直线
for(const auto& point : m_Points)
pDC->LineTo(point);
pDC->SelectObject(pOldPen); //恢复旧画笔
}
CCurve::CCurve(const CPoint& start, const CPoint& second, COLORREF color) : CElement{ start,color }
{
m_Points.push_back(second);
m_EnclosingRect = CRect{ (std::min)(start.x,second.x),(std::min)(start.y,second.y),(std::max)(start.x,second.x),(std::max)(start.y,second.y) };
m_EnclosingRect.InflateRect(m_PenWidth, m_PenWidth); // 增加CRect的宽度和高度 。
}
void CCurve::AddSegment(const CPoint& point)
{
m_Points.push_back(point);
m_EnclosingRect.DeflateRect(m_PenWidth, m_PenWidth); //减小 CRect 的宽度和高度。
m_EnclosingRect = CRect{ (std::min)(point.x,m_EnclosingRect.left),(std::min)(point.y,m_EnclosingRect.top),(std::max)(point.x,m_EnclosingRect.right),(std::max)(point.y,m_EnclosingRect.bottom) };
m_EnclosingRect.InflateRect(m_PenWidth, m_PenWidth); // 增加CRect的宽度和高度 。
}
CCurve::CCurve()
{
}
鼠标消息处理程序
// SketcherView.cpp:
#include "Line.h"
#include "Rectangle.h"
#include "Circle.h"
#include "Curve.h"
void CSketcherView::OnMouseMove(UINT nFlags, CPoint point)
{
//CClientDC 类:负责在构建时调用 Windows 函数GetDC ,在销毁时调用ReleaseDC。 CClientDC(CWnd* pWnd);
CClientDC aDC{ this }; //创建设备上下文
if (nFlags & MK_LBUTTON) //MK_LBUTTON:按下鼠标左键
{
m_SecondPoint = point;//保存当前坐标
if (ElementType::CURVE == GetDocument()->GetElementType()) //线段 CDocument* m_pDocument;
{
std::dynamic_pointer_cast(m_pTempElement)->AddSegment(m_SecondPoint); //添加线段
m_pTempElement->Draw(&aDC); //绘图
return;
}
else //临时元素存在,不是曲线
{
aDC.SetROP2(R2_NOTXORPEN); //SetROP2函数设置当前的前景混合模式。
m_pTempElement->Draw(&aDC); //绘图
}
}
//如果临时元素存在,但不是曲线,或没有临时元素存在
m_pTempElement = CreateElement(); //创建新元素
m_pTempElement->Draw(&aDC); //按正常方式绘图
//CView::OnMouseMove(nFlags, point);
}
// SketcherView.h:
class CSketcherView : public CView
{
protected:
std::shared_ptr CreateElement(void) const; //在堆上创建一个新元素
}
// SketcherView.cpp
//创建元素
std::shared_ptr CSketcherView::CreateElement(void) const
{
CSketcherDoc* pDoc = GetDocument(); //获取指向视图文档的指针
ASSERT_VALID(pDoc);//检查指针有效
COLORREF color{static_cast(pDoc->GetElementColor())}; //获取颜色指针
switch (pDoc->GetElementType())
{
case ElementType::RECTANGLE:
return std::make_shared(m_FirstPoint,m_SecondPoint,color); // std::make_shared 可以返回一个指定类型的 std::shared_ptr ,执行构造
case ElementType::CIRCLE:
return std::make_shared(m_FirstPoint, m_SecondPoint, color);
case ElementType::CURVE:
return std::make_shared(m_FirstPoint, m_SecondPoint, color);
case ElementType::LINE:
return std::make_shared(m_FirstPoint, m_SecondPoint, color);
default:
AfxMessageBox(_T("Bad element code"),MB_OK);
AfxAbort();
return nullptr;
}
}
//SketcherDoc.h
#include "Element.h" //添加
#include //添加
#include //添加
class CSketcherDoc : public CDocument
{
protected:
std::list> m_Sketch; //添加
// 实现 Implementation
public:
void AddElement(std::list>& pElement); //添加元素
void DeleteElement(std::list>& pElement); //删除元素
}
// SketcherDoc.cpp:
void CSketcherDoc::AddElement(std::shared_ptr& pElement)
{
m_Sketch.push_back(pElement);
}
void CSketcherDoc::DeleteElement(std::shared_ptr& pElement)
{
m_Sketch.remove(pElement);
}
// SketcherView.cpp
void CSketcherView::OnLButtonUp(UINT nFlags, CPoint point)
{
if (m_pTempElement)
{
GetDocument()->AddElement(m_pTempElement); //添加Element元素
InvalidateRect(&m_pTempElement->GetEnclosingRect()); //CWnd::InvalidateRect通过将该矩形添加到 CWnd 更新区域,使给定矩形中的工作区失效。
m_pTempElement.reset(); // 释放资源并转换为空的 shared_ptr 对象
}
//CView::OnLButtonUp(nFlags, point);
}
让视图对象获得绘制草图所需的迭代器
//SketcherDoc.h
class CSketcherDoc : public CDocument
{
public:
// 为 sketch提供一个开始迭代器
std::list>::const_iterator begin() const;
// 为sketch提供一个结束迭代器
std::list>::const_iterator end() const;
}
// SketcherDoc.cpp:
// 为 sketch提供一个开始迭代器
std::list>::const_iterator CSketcherDoc::begin() const
{
return std::begin(m_Sketch);
}
// 为sketch提供一个结束迭代器
std::list>::const_iterator CSketcherDoc::end() const
{
return std::end(m_Sketch);
}
// SketcherView.cpp
// CSketcherView 绘图
void CSketcherView::OnDraw(CDC* pDC)
{
CSketcherDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
//绘制
for (auto iter=pDoc->begin();iter != pDoc->end();++iter) //遍历
{
for (const auto& pElement : *pDoc) //遍历元素
{
if (pDC->RectVisible(pElement->GetEnclosingRect())) //DC::RectVisible 确定给定矩形的任何部分是否位于显示上下文的剪辑区域中。
pElement->Draw(pDC);
}
}
}
运行并不正常,捕获鼠标消息。
// SketcherView.cpp
void CSketcherView::OnLButtonDown(UINT nFlags, CPoint point)
{
m_FirstPoint = point; //记录起点
SetCapture(); //导致所有后续鼠标输入都发送到当前 CWnd 对象,而不考虑光标的位置。
//CView::OnLButtonDown(nFlags, point);
}
// SketcherView.cpp
void CSketcherView::OnLButtonUp(UINT nFlags, CPoint point)
{
if (this == GetCapture()) //GetCapture 检索具有鼠标捕获的 CWnd。
ReleaseCapture(); //从当前线程中的窗口释放鼠标捕获并恢复正常的鼠标输入处理。
if (m_pTempElement)
{
GetDocument()->AddElement(m_pTempElement); //添加Element元素
InvalidateRect(&m_pTempElement->GetEnclosingRect()); //CWnd::InvalidateRect通过将该矩形添加到 CWnd 更新区域,使给定矩形中的工作区失效。
m_pTempElement.reset(); // 释放资源并转换为空的 shared_ptr 对象
}
//CView::OnLButtonUp(nFlags, point);
}
// SketcherView.cpp
void CSketcherView::OnMouseMove(UINT nFlags, CPoint point)
{
//CClientDC 类:负责在构建时调用 Windows 函数GetDC ,在销毁时调用ReleaseDC。 CClientDC(CWnd* pWnd);
CClientDC aDC{ this }; //创建设备上下文
if ((nFlags & MK_LBUTTON) && (this == GetCapture())) //MK_LBUTTON:按下鼠标左键
{
m_SecondPoint = point;//保存当前坐标
if (m_pTempElement)
{
if (ElementType::CURVE == GetDocument()->GetElementType()) //线段 CDocument* m_pDocument;
{
std::dynamic_pointer_cast(m_pTempElement)->AddSegment(m_SecondPoint); //添加线段
m_pTempElement->Draw(&aDC); //绘图
return;
}
else //临时元素存在,不是曲线
{
aDC.SetROP2(R2_NOTXORPEN); //SetROP2函数设置当前的前景混合模式。
m_pTempElement->Draw(&aDC); //绘图
}
}
//如果临时元素存在,但不是曲线,或没有临时元素存在
m_pTempElement = CreateElement(); //创建新元素
m_pTempElement->Draw(&aDC); //按正常方式绘图
}
//CView::OnMouseMove(nFlags, point);
}