vc 画图控件

文章来源:http://blog.csdn.net/qiuchengw/article/details/6324791

这是的确重复造轮子,但是造了一个更适合自己使用的轮子.

项目的需要,要实现一个画图的功能,在网上搜了N久的代码,可惜那些代码都太"粗糙"了,没有符合自己要求的,所以只好自己写了一个 .

这是效果图(图像可填充背景色,也可只有边界色,也可两者都有):

 

 

 

这是代码(由于是自己项目的一部分,就不传工程了):

QShap 是基类, 继承此类,可实现自己的图形.本代码只实现了自己需要的一些图形,line/curve/ellipse/rectangle

 

// !!!!注意

// 此份画图的代码只是为了 "看起来像" ,图形大小不是精确的(会有1到2个像素的大小偏差):

 

.h

#pragma once
#include "afxcolorbutton.h"
#include "ResLoader.h"
#include "afxlistctrl.h"
#include "qstatic.h"
// !!!!注意
// 此份画图的代码只是为了 "看起来像" ,图形大小不是精确的(会有1到2个像素的大小偏差)
const CPoint POINT_NULL = CPoint(-1,-1);
const CRect RECT_NULL = CRect(-1,-1,-1,-1);
const CRect RECT_EMPTY = CRect(0,0,0,0);
enum ENUM_SHAPE_TYPE
{
	SHAPE_NONE = 1,
	SHAPE_RECT,
	SHAPE_ELLIPSE,
	SHAPE_CURVE,
	SHAPE_LINE,
	SHAPE_ICON,
	MAX_SHAPE_VALUE,
};
// Shape 及其派生类都是用于CPaintPane的专用类.
class QShape
{
public:
	QShape(ENUM_SHAPE_TYPE eShape);
	virtual ~QShape(){}
	virtual void Draw(CDC *pDC) = 0;
	virtual CRect GetRect()const; 
	COLORREF GetBorderColor()const { return m_crBoder; };
	COLORREF GetFillColor()const { return m_crFill; }
	void SetBorderColor(COLORREF cr=COLOR_BLACK) { m_crBoder = cr; }
	void SetFillColor(COLORREF cr=COLOR_NONE) { m_crFill = cr; }
	ENUM_SHAPE_TYPE GetShape()const { return m_ShapeType; }
	// 在鼠标按下的时候调用SetStartPoint,
	// 移动的调用UpdateEndPoint
	// 弹起的时候调用SetEndPoint
	virtual void SetStartPoint(const CPoint&point) {ASSERT(POINT_NULL != point); m_pt1 = point; };
	virtual void SetEndPoint(const CPoint&point){ASSERT(POINT_NULL != point); m_pt2 = point; };
	virtual void UpdateEndPoint(const CPoint&point){ SetEndPoint(point); };
public:
	CPoint			m_pt1,m_pt2;
	COLORREF		m_crBoder;
	COLORREF		m_crFill;
private:
	ENUM_SHAPE_TYPE		m_ShapeType;
};
// the shape factory
QShape* CreateShape( ENUM_SHAPE_TYPE eShape ,LPARAM lParam = 0);
class QRectangle : public QShape
{
public:
	QRectangle():QShape(SHAPE_RECT){ }
	virtual void Draw(CDC *pDC);
};
class QEllipse : public QShape
{
public:
	QEllipse():QShape(SHAPE_ELLIPSE){}
	virtual void Draw(CDC *pDC);
};
class QCurve : public QShape
{
public:
	QCurve();
	virtual void Draw(CDC *pDC);
	// 在curve中,pt1,和pt2标志曲线所占据的矩形,不是开始和结束点
	virtual void UpdateEndPoint(const CPoint&point);
	virtual void SetEndPoint(const CPoint&point) { UpdateEndPoint(point); };
	virtual CRect GetRect()const;
private:
	typedef CArray<CPoint,CPoint>	CurvePoint;
	CurvePoint	m_Points;
};
class QLine : public QShape
{
public :
	QLine():QShape(SHAPE_LINE) { }
	virtual void Draw(CDC *pDC);
};
class QIcon : public QShape
{
public:
	QIcon(HICON hIcon);
	virtual void Draw(CDC *pDC);
	virtual CRect GetRect()const ;
	virtual void SetEndPoint(const CPoint&point){};
	virtual void UpdateEndPoint(const CPoint&point){ };
private:
	HICON	m_hIcon;
};
// CPainterPane
class CPainterPane : public CWnd
{
	typedef std::vector<QShape *>  SHAPES;
	typedef SHAPES::iterator  ShapeItr;
	DECLARE_DYNAMIC(CPainterPane)
public:
	CPainterPane();
	virtual ~CPainterPane();
	BOOL SetMainBk( LPCTSTR pszDll,UINT nID,LPCTSTR pszType,COLORREF crTrans=COLOR_BLACK );
	BOOL InitPane(CRect rect,CWnd *pParent,UINT nID);
	void SetDrawing( ENUM_SHAPE_TYPE eShape,LPARAM lParam =0);
	void SetShapeColor(COLORREF crBorder,COLORREF crFill=COLOR_NONE);
	// 清除当前所画的所有内容,不包括背景
	void ClearDrawed(); 
	// 撤销nTime次的操作
	void Undo(int nTimes = 1);
	int GetShapesCount() { return m_Shapes.size(); }
protected:
	void DrawClient(CDC *pDC,const CRect &rect);
	// 在内存dc中画
	void DrawMainBack(CDC* pDC , const CRect &rect );
	void DrawShapes( CDC* pDC ,const CRect &rect );
	void CopyDC(CDC *pDst,CDC *pSrc,const CRect &rect);
	// 将pDCSrc 更新显示到界面
	void CopyDCToSurface(CDC *pDCSrc,const CRect&rect);
	void Reset();
	BOOL _IsDrawing()const;
	BOOL _BeginDrawing( CPoint ptStart );
	void _UpdateDrawing(CPoint ptEnd);
	void _EndDrawing(CPoint ptEnd);
protected:
	DECLARE_MESSAGE_MAP()
	afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
	afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
	afx_msg void OnMouseMove(UINT nFlags, CPoint point);
	afx_msg BOOL OnEraseBkgnd(CDC* pDC);
	afx_msg void OnPaint();
	class MouseTrack
	{
	protected:
		typedef std::vector<CPoint> LstTrack;
		typedef LstTrack::iterator LstTrackItr;
		LstTrack	m_LstTrack; // 第一个点是鼠标按下的位置,最后一个点是最后一次鼠标移动到的位置
		BOOL		m_bDown;	// 是否已经按下? 需要调用Down设置
	public:
		MouseTrack() { m_bDown = FALSE; }
		void Down(CPoint pt) { Reset(); m_LstTrack.push_back(pt); m_bDown = TRUE; }
		void Move(CPoint pt) { ASSERT(m_bDown); m_LstTrack.push_back(pt); }
		void Up(CPoint pt) { m_LstTrack.push_back(pt); m_bDown = FALSE; }
		void Reset() { m_bDown = FALSE; m_LstTrack.clear(); }
		BOOL IsDown()const { return m_bDown; }
		CPoint PointDown()const 
		{ 
			if (m_LstTrack.size() > 0)
				return m_LstTrack[0]; 
			return POINT_NULL;
		}
		// idx 既可以是正数索引,也可以是负数索引,不能为0
		// -1  为最后一次的矩形 -2 倒数第二次
		// 1  第一次, 2 第二次
		CPoint PointX(int idx=-1)const
		{
			LstTrack::size_type n = m_LstTrack.size();
			idx = (idx < 0) ? n + idx : idx;
			if (n < 2 || idx <= 0 || idx >= n)
				return POINT_NULL;
			return m_LstTrack[idx];
		}
		// 获取鼠标按下和第idx次移动的矩形, 
		// idx 既可以是正数索引,也可以是负数索引,不能为0
		// -1  为最后一次的矩形 -2 倒数第二次
		// 1  第一次, 2 第二次
		CRect GetRect(int idx = -1)const 
		{
			CPoint pt2 = PointX(idx);
			if (POINT_NULL == pt2)
				return RECT_NULL;
			CRect rect(m_LstTrack[0],pt2);
			rect.NormalizeRect();
			return rect;
		}
	};
	MouseTrack	m_MouseTrack;
	SHAPES	m_Shapes;
private:
	HBITMAP		m_hbmpMainBack;
	CBitmap		m_bmpMemBack;
	CDC		m_dcMemBack;
	CBitmap		m_bmpMemDrawed;	
	CDC		m_dcMemDrawed;
	CBitmap		m_bmpMemDrawing;
	CDC		m_dcMemDrawing;
	HCURSOR		m_hCurCross,m_hCurArrow;
	ENUM_SHAPE_TYPE	m_eDrawing;	// 
	LPARAM		m_lShapeParam;
	QShape*		m_pShapeDrawing;	// 当前正在画的图形
	COLORREF	m_crShapeBack,m_crShapeBorder;
};
// CPainterList
struct _SHAPE_ITEMS 
{
	int	iImg;
	CString sItem;
	DWORD	dwData;
};
enum 
{
	ACTION_CLEAR = MAX_SHAPE_VALUE + 1,
	ACTION_UNDO,
	ACTION_SAVE,
};
class CPainterList : public CMFCListCtrl
{
	DECLARE_DYNAMIC(CPainterList)
public:
	CPainterList();
	virtual ~CPainterList();
	void SetAssoicatePainter(CPainterPane *pPainter) { m_pPainter = pPainter; };
	BOOL Init( UINT nImgeList ,COLORREF crMask);
	void AddButton(LPCTSTR sItem,int iImage,DWORD dwData);
	void AddButton(const _SHAPE_ITEMS *pItem);
protected:
	DECLARE_MESSAGE_MAP()
	afx_msg void OnNMClick(NMHDR *pNMHDR, LRESULT *pResult);
	afx_msg void OnLvnItemchanging(NMHDR *pNMHDR, LRESULT *pResult);
	virtual COLORREF OnGetCellTextColor(int iRow, int iCol);
	virtual COLORREF OnGetCellBkColor(int iRow, int iCol);
	virtual void PreSubclassWindow();
	BOOL IsSelected(int iRow);
private:
	CPainterPane*	m_pPainter;
	CImageList	m_ImageList;	
};
// CPainterDlg 对话框
class CPainterDlg : public CDialogEx
{
	DECLARE_DYNAMIC(CPainterDlg)
public:
	CPainterDlg(CWnd* pParent = NULL);   // 标准构造函数
	virtual ~CPainterDlg();
// 对话框数据
	enum { IDD = IDD_PAINTER };
	void SetImageParams( LPCTSTR pszDll,LPCTSTR pszType,UINT nImgID ,COLORREF crTrans);
protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持
	virtual BOOL OnInitDialog();
	afx_msg void OnColorChanged();
	afx_msg void OnPaint();
	DECLARE_MESSAGE_MAP()
	enum
	{
		IDC_PAINTPANE_COLORBAR = 111,
	};
	
	static const _SHAPE_ITEMS	scm_ShapeItems[];
private:
	CMFCColorBar	 m_ColorBar;
	CPainterPane	m_Painter;
	CString		m_sDll;
	CString		m_sType;
	UINT		m_nImgeID;
	COLORREF	m_crTrans;
	CPainterList	m_ListBtns;
	QGroupBox	m_Box1;
	CImageList	m_ImageList;
	QGroupBox m_Box2;
};

 

 

.cpp

// PainterDlg.cpp : 实现文件
//
#include "stdafx.h"
#include "QAAS.h"
#include "PainterDlg.h"
#include "afxdialogex.h"
QShape* CreateShape( ENUM_SHAPE_TYPE eShape ,LPARAM lParam)
{
	if (SHAPE_NONE == eShape)
	{
		ASSERT(FALSE);
		return NULL;
	}
	QShape * pShape = NULL;
	switch (eShape)
	{
	case SHAPE_RECT:
		{
			pShape = new QRectangle();
			break;
		}
	case SHAPE_ELLIPSE:
		{
			pShape = new QEllipse();
			break;
		}
	case SHAPE_LINE:
		{
			pShape = new QLine();
			break;
		}
	case SHAPE_CURVE:
		{
			pShape = new QCurve();
			break;
		}
	case SHAPE_ICON:
		{
			pShape = new QIcon((HICON)lParam);
			break;
		}
	default:
		{
			ASSERT(FALSE);
			pShape = NULL;
			break;
		}
	}
	return pShape;
}
QShape::QShape( ENUM_SHAPE_TYPE eShape )
	:m_ShapeType(eShape)
{
	m_crBoder = m_crFill = COLOR_NONE; 
	m_pt1 = m_pt2 = POINT_NULL; 
}
CRect QShape::GetRect() const
{
	CRect rect(m_pt1,m_pt2);
	rect.NormalizeRect();
	return rect;
}
void QRectangle::Draw( CDC *pDC )
{
	ASSERT(pDC != NULL);
	CRect rect = GetRect();
	if (COLOR_NONE != m_crFill)
	{
		pDC->FillSolidRect(&rect,m_crFill);
	}
	pDC->FrameRect(&rect,&CBrush(m_crBoder));
}
void QEllipse::Draw( CDC *pDC )
{
	ASSERT(pDC != NULL);
	using namespace Gdiplus;
	CRect rect = GetRect();
	rect.DeflateRect(1,1);	// 防止切边问题,注释此行可以重现重现问题
	Gdiplus::Graphics gra(pDC->GetSafeHdc());
	gra.SetSmoothingMode(SmoothingModeAntiAlias);	// 开启反锯齿效果
	if (COLOR_NONE != m_crFill)
	{
		gra.FillEllipse(&SolidBrush(DwordToColor(m_crFill)),(RectF)QRect(rect));
	}
	Pen pen(DwordToColor(m_crBoder));
	gra.DrawEllipse(&pen,(RectF)QRect(rect));
}
// 曲线实现比较特殊,要特别处理
QCurve::QCurve():QShape(SHAPE_CURVE) 
{
	// 在curve中,pt1,和pt2标志曲线所占据的矩形,不是开始和结束点
	m_pt1.x = m_pt1.y = INT_MAX; 
	m_pt2.x = m_pt2.y = -1;
}
CRect QCurve::GetRect()const
{
	// 还没有添加点
	if ((m_pt1.x == INT_MAX) || (m_pt2.x == -1))
	{
		return CRect(0,0,0,0);
	}
	CRect rect = QShape::GetRect();
	rect.InflateRect(1,1);	// 为防止切边
	return rect;
}
void QCurve::Draw( CDC *pDC )
{
	int nCount = m_Points.GetCount();
	if (nCount < 2)
		return;
	
	CPen pen(PS_SOLID,1,m_crBoder);
	CPen *pOldPen = (CPen*)pDC->SelectObject(&pen);
	pDC->MoveTo(m_Points.GetAt(0));
	for (int i = 1; i < nCount; i++)
	{
		pDC->LineTo(m_Points[i]);
	}
	pDC->SelectObject(pOldPen);
}
void QCurve::UpdateEndPoint( const CPoint&point )
{
	// 在curve中,pt1,和pt2标志曲线所占据的矩形,不是开始和结束点
	m_pt1.x = min(m_pt1.x,point.x);
	m_pt1.y = min(m_pt1.y,point.y);
	m_pt2.x = max(m_pt2.x,point.x);
	m_pt2.y = max(m_pt2.y,point.y);
	m_Points.Add(point);
}
void QLine::Draw( CDC *pDC )
{
	if (POINT_NULL == m_pt1 || POINT_NULL == m_pt2)
		return;
	CAADraw aa;
	aa.DrawLine(pDC->GetSafeHdc(),m_pt1.x,m_pt1.y,m_pt2.x,m_pt2.y,m_crBoder);
}
QIcon::QIcon( HICON hIcon )
	:QShape(SHAPE_ICON)
{
	ASSERT(hIcon != NULL);
	m_hIcon = hIcon;
}
void QIcon::Draw( CDC *pDC )
{
	if (NULL != m_hIcon)
	{
		pDC->DrawIcon(m_pt1,m_hIcon);
	}
}
CRect QIcon::GetRect() const
{
	if (NULL != m_hIcon)
	{
		CRect rect;
		ICONINFO ii;
		if (GetIconInfo(m_hIcon,&ii))
		{
			return CRect(m_pt1,CPoint(ii.xHotspot * 2 + m_pt1.x, ii.yHotspot * 2 + m_pt1.y));
		}
	}
	return RECT_EMPTY;
}
//////////////////////////////////////////////////////////////////
// brief	:	2011/03/25 
// copyright:	qiuchengw @ 2011
//////////////////////////////////////////////////////////////////
// CPainterPane
IMPLEMENT_DYNAMIC(CPainterPane, CWnd)
CPainterPane::CPainterPane()
{
	m_crShapeBack = COLOR_NONE;
	m_crShapeBorder = COLOR_BLACK;
	m_eDrawing = SHAPE_NONE;
	m_pShapeDrawing = NULL;
	m_hCurCross = NULL;
	m_hCurArrow = NULL;
}
CPainterPane::~CPainterPane()
{
	Reset();
	DeleteObject(m_hCurCross);
	DeleteObject(m_hCurArrow);
}
void CPainterPane::Reset()
{
	if (m_dcMemBack.m_hDC)
		m_dcMemBack.DeleteDC();
	if (m_dcMemDrawed.m_hDC)
		m_dcMemDrawed.DeleteDC();
	if (m_dcMemDrawing.m_hDC)
		m_dcMemDrawing.DeleteDC();
	if (m_bmpMemDrawed.m_hObject)
		m_bmpMemDrawed.DeleteObject();
	if (m_bmpMemBack.m_hObject)
		m_bmpMemBack.DeleteObject();
	if (m_bmpMemDrawing.m_hObject)
		m_bmpMemDrawing.DeleteObject();
	for (ShapeItr itr = _BeginItr(m_Shapes); itr != _EndItr(m_Shapes); ++itr)
	{
		delete *itr;
	}
	m_Shapes.clear();
	m_eDrawing = SHAPE_NONE;
	m_pShapeDrawing = NULL;
	m_crShapeBack = COLOR_NONE;
	m_crShapeBorder = COLOR_BLACK;
}
BEGIN_MESSAGE_MAP(CPainterPane, CWnd)
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONUP()
	ON_WM_MOUSEMOVE()
	ON_WM_ERASEBKGND()
	ON_WM_PAINT()
END_MESSAGE_MAP()
// CPainterPane 消息处理程序
void CPainterPane::OnLButtonDown(UINT nFlags, CPoint point)
{
	if (SHAPE_NONE != m_eDrawing)
	{
		if (!_BeginDrawing(point))
		{
			ASSERT(FALSE);
//			SetDrawing(SHAPE_NONE);
		}
	}
}
void CPainterPane::OnLButtonUp(UINT nFlags, CPoint point)
{
	if (_IsDrawing())
	{
		_EndDrawing(point);
	}
}
void CPainterPane::OnMouseMove(UINT nFlags, CPoint point)
{
	SetCursor((SHAPE_NONE != m_eDrawing) ? m_hCurCross : m_hCurArrow);
	if (_IsDrawing())
	{
		_UpdateDrawing(point);
	}
}
BOOL CPainterPane::_BeginDrawing( CPoint ptStart )
{
	ASSERT(SHAPE_NONE != m_eDrawing);
	CRect rcClip;
	GetWindowRect(&rcClip);
	// 矩形各边界减小1为了避免当鼠标移动到此窗体的边框上
	// 然后释放鼠标按钮就接收不到WM_LBUTTONUP消息的情况
	rcClip.DeflateRect(1,1);
	if ( ClipCursor(&rcClip))
	{
		if (NULL != (m_pShapeDrawing = CreateShape(m_eDrawing,m_lShapeParam)))
		{
			m_pShapeDrawing->SetStartPoint(ptStart);
			m_pShapeDrawing->SetBorderColor(m_crShapeBorder);
			m_pShapeDrawing->SetFillColor(m_crShapeBack);
			m_MouseTrack.Down(ptStart);
			return TRUE;
		}
	}
	// 新创建响应的图形
	ClipCursor(NULL);
	return TRUE;
}
void CPainterPane::_UpdateDrawing( CPoint ptMove )
{
	ASSERT(m_pShapeDrawing != NULL);
	// 鼠标按下到上次移动的矩形位置
	CRect rcLast = m_MouseTrack.GetRect();
	// 鼠标按下到此次移动到的矩形位置
	m_MouseTrack.Move(ptMove);
	// 设置图形的矩形为当前的移动的位置
	m_pShapeDrawing->UpdateEndPoint(ptMove);
	// 当前图像的矩形
	CRect rcDrawing = m_pShapeDrawing->GetRect();
	// 取两个矩形的并集进行更新,它们的并集就是当前的脏矩形
	if (rcDrawing.UnionRect(&rcDrawing,&rcLast))
	{
		// 边界增大一点是为了不使图形切边
		rcDrawing.InflateRect(1,1);
		// 先拷贝需要更新区域的已画好的图形到正在画的区域中
		CopyDC(&m_dcMemDrawing,&m_dcMemDrawed,rcDrawing);
		// 画图形
		m_pShapeDrawing->Draw(&m_dcMemDrawing);
		// 更新到显示dc,
		// 只有当鼠标左键弹起,标志这个图形画好的时候才能更新到m_dcMemDrawed 上
		CopyDCToSurface(&m_dcMemDrawing,rcDrawing);
	}
}
void CPainterPane::CopyDCToSurface(CDC *pDC,const CRect&rect)
{
	CDC *pWndDC = GetDC();
	if (pDC->GetSafeHdc() != pWndDC->GetSafeHdc())
	{
		CopyDC(pWndDC,pDC,rect);
	}
	ReleaseDC(pWndDC);
}
void CPainterPane::_EndDrawing( CPoint ptEnd )
{
	// 画图形已经完成了.
	ClipCursor(NULL);
	_UpdateDrawing(ptEnd);
	// 更新图像最后的坐标
	m_pShapeDrawing->SetEndPoint(ptEnd);
	// 此处或许会有一个像素的偏差,因为没有再次调用_UpdateDrawing(ptEnd);
	m_MouseTrack.Up(ptEnd);
	CRect rect = m_pShapeDrawing->GetRect();
	// 保存所画的图像到列表记录
	m_Shapes.push_back(m_pShapeDrawing);
	// 所画的图像更新到已画的图的内存dc中
	CopyDC(&m_dcMemDrawed,&m_dcMemDrawing,rect);
	// 将更新好的图像显示出来
	CopyDCToSurface(&m_dcMemDrawed,rect);
}
BOOL CPainterPane::InitPane( CRect rect,CWnd *pParent,UINT nID )
{
	m_hCurCross = AfxGetApp()->LoadStandardCursor(IDC_CROSS);
	m_hCurArrow = AfxGetApp()->LoadStandardCursor(IDC_ARROW);
	LPCTSTR pszClass = AfxRegisterWndClass(CS_VREDRAW|CS_HREDRAW,m_hCurArrow);
	if (!CWnd::Create(pszClass,L"",WS_VISIBLE|WS_CHILD|WS_BORDER,rect,pParent,nID))
		return FALSE;
	return TRUE;
}
BOOL CPainterPane::OnEraseBkgnd(CDC* pDC)
{
	return TRUE;
}
void CPainterPane::OnPaint()
{
	CPaintDC dc(this); // device context for painting
	
	CRect rect;
	dc.GetClipBox(&rect);
	DrawClient(&dc,rect);
}
void CPainterPane::DrawClient( CDC *pDC,const CRect &rect )
{
	ASSERT(pDC != NULL);
	// 使用背景清除需要更新的区域
// 	DrawMainBack(&m_dcMemDrawed,rect);
// 	DrawShapes(&m_dcMemDrawed,rect);
	// 从内存中拷贝到显示
	CopyDC(pDC,&m_dcMemDrawed,rect);
}
void CPainterPane::DrawShapes( CDC* pDC ,const CRect &rect )
{
	CRect rc; 
	for (ShapeItr itr = _BeginItr(m_Shapes); itr != _EndItr(m_Shapes); ++itr)
	{
		rc = rect;
		if (rc.IntersectRect(&rc,&rect))
		{
			(*itr)->Draw(pDC);
		}
	}
}
void CPainterPane::DrawMainBack(CDC* pDC , const CRect &rect )
{
	// 使用背景清除pDC
	CopyDC(pDC,&m_dcMemBack,rect);
}
BOOL CPainterPane::SetMainBk( LPCTSTR pszDll,UINT nID,LPCTSTR pszType,COLORREF crTrans )
{
	Reset();
	Gdiplus::Image *pImg = NULL;
	if (CResLoader::LoadFromDll(pszDll,pszType,nID,pImg))
	{
		CDC *pDC = GetDC();
		m_dcMemBack.CreateCompatibleDC(pDC);
		m_bmpMemBack.CreateCompatibleBitmap(pDC,pImg->GetWidth(),pImg->GetHeight());
		m_dcMemBack.SelectObject(&m_bmpMemBack);
		m_dcMemDrawed.CreateCompatibleDC(pDC);
		m_bmpMemDrawed.CreateCompatibleBitmap(pDC,pImg->GetWidth(),pImg->GetHeight());
		m_dcMemDrawed.SelectObject(&m_bmpMemDrawed);
		m_dcMemDrawing.CreateCompatibleDC(pDC);
		m_bmpMemDrawing.CreateCompatibleBitmap(pDC,pImg->GetWidth(),pImg->GetHeight());
		m_dcMemDrawing.SelectObject(&m_bmpMemDrawing);
		ImageAttributes imgati;
		if (COLOR_NONE != crTrans)
		{
			imgati.SetColorKey(DwordToColor(crTrans),DwordToColor(crTrans));
		}
		Gdiplus::Graphics(m_dcMemBack.m_hDC).DrawImage(pImg,QRect(0,0,pImg->GetWidth(),pImg->GetHeight()),
			0,0,pImg->GetWidth(),pImg->GetHeight(),UnitPixel,&imgati);
		CopyDC(&m_dcMemDrawed,&m_dcMemBack,RECT_EMPTY);
		CopyDC(&m_dcMemDrawing,&m_dcMemBack,RECT_EMPTY);
		CopyDC(pDC,&m_dcMemBack,RECT_EMPTY);
		ReleaseDC(pDC);
		return TRUE;
	}
	return FALSE;
}
void CPainterPane::CopyDC( CDC *pDst,CDC *pSrc,const CRect &rect )
{
	ASSERT(pDst != NULL && pSrc != NULL);
	if (pDst->GetSafeHdc() != NULL && pSrc->GetSafeHdc() != NULL)
	{
		CRect rc = rect;
		if (rc.Width() == 0 || rc.Height() == 0)
		{
			rc.left = rc.top = 0;
			rc.bottom = rc.right = 1024;		// 只是假设背景图像不会大于 1024 * 1024
		}
		pDst->BitBlt(rc.left,rc.top,rc.Width(),rc.Height(),pSrc,rc.left,rc.top,SRCCOPY);
	}
}
void CPainterPane::SetDrawing( ENUM_SHAPE_TYPE eShape,LPARAM lParam )
{
	ASSERT(!_IsDrawing());
	m_eDrawing = eShape;
	m_lShapeParam = lParam;
}
BOOL CPainterPane::_IsDrawing() const
{
	/*
#ifdef _DEBUG
	if (m_pShapeDrawing != NULL)
	{
		ASSERT(m_pShapeDrawing->GetShape() == m_eDrawing);
		return TRUE;
	}
	return FALSE;
#else
	return (m_pShapeDrawing != NULL);
#endif
	*/
	return (m_eDrawing != SHAPE_NONE) && (m_MouseTrack.IsDown());
}
void CPainterPane::SetShapeColor( COLORREF crBorder,COLORREF crFill/*=COLOR_NONE*/ )
{
	m_crShapeBorder = crBorder;
	m_crShapeBack = crFill;
}
void CPainterPane::ClearDrawed()
{
	if (GetShapesCount() > 0)
	{
		for (ShapeItr itr = _BeginItr(m_Shapes); itr != _EndItr(m_Shapes); ++itr)
		{ // 将所有的已画的图形都删掉
			delete *itr;
		}
		m_Shapes.clear();
		// 使用主背景清除已画的图形
		CopyDC(&m_dcMemDrawed,&m_dcMemBack,RECT_EMPTY);
		CopyDC(&m_dcMemDrawing,&m_dcMemBack,RECT_EMPTY);
		// 更新到显示
		Invalidate(TRUE);
	}
	ASSERT(GetShapesCount() == 0);
}
void CPainterPane::Undo( int nTimes /*= 1*/ )
{
	ASSERT(nTimes > 0);
	
	if (GetShapesCount() > 0 )
	{
		CRect rcUpdate = RECT_EMPTY; // 所要清除的图像的区域并集,最后要更新
		for (SHAPES::size_type i = GetShapesCount() - 1 ; i >= 0 && nTimes > 0;  --i,--nTimes)
		{
			rcUpdate.UnionRect(&rcUpdate,&(m_Shapes[i]->GetRect()));
			delete m_Shapes[i];
			m_Shapes.pop_back();
		}
		// 更新已清除的图形的并集区域
		rcUpdate.InflateRect(1,1);
		DrawMainBack(&m_dcMemDrawed,rcUpdate);
		DrawShapes(&m_dcMemDrawed,rcUpdate);
		InvalidateRect(&rcUpdate,FALSE);
	}
}
//////////////////////////////////////////////////////////////////
// brief	:	2011/03/24 
// copyright:	qiuchengw @ 2011
//////////////////////////////////////////////////////////////////
// CPainterDlg 对话框
const _SHAPE_ITEMS CPainterDlg::scm_ShapeItems[] = 
{
	{ 0, L"直线", SHAPE_LINE },
	{ 1, L"矩形", SHAPE_RECT },
	{ 2, L"椭圆", SHAPE_ELLIPSE },
	{ 3, L"曲线", SHAPE_CURVE },
	{ 4, L"撤销上一步",ACTION_UNDO  },
	{ 5, L"清除所有", ACTION_CLEAR },
	{ 6, L"保存",  ACTION_SAVE },
};
IMPLEMENT_DYNAMIC(CPainterDlg, CDialogEx)
CPainterDlg::CPainterDlg(CWnd* pParent /*=NULL*/)
	: CDialogEx(CPainterDlg::IDD, pParent)
{
}
CPainterDlg::~CPainterDlg()
{
}
void CPainterDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_LIST_BTNS, m_ListBtns);
	DDX_Control(pDX, IDC_BOX1, m_Box1);
	DDX_Control(pDX, IDC_BOX2, m_Box2);
}
BEGIN_MESSAGE_MAP(CPainterDlg, CDialogEx)
	ON_BN_CLICKED(IDC_PAINTPANE_COLORBAR, &CPainterDlg::OnColorChanged)
	ON_WM_PAINT()
END_MESSAGE_MAP()
BOOL CPainterDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();
	SetBackgroundColor(RGB(240,240,240));
	CRect rect = GetDlgItemRect(IDC_RECT_COLORBAR,this);
	m_ColorBar.CreateControl(this,rect,IDC_PAINTPANE_COLORBAR,5);
	m_ColorBar.ShowWindow(SW_SHOW);
	m_ColorBar.SetColor(RGB(0,0,0));
	m_Box1.SetHeaderHeight(1);
	m_Box1.SetBorderColor(RGB(130,135,144));
	m_Box2.SetHeaderHeight(1);
	m_Box2.SetBorderColor(RGB(130,135,144));
	rect = GetDlgItemRect(IDC_RECT_PAINTPANE,this);
	if (!m_Painter.InitPane(rect,this,112))
		return FALSE;
	m_Painter.ShowWindow(SW_SHOW);
	m_Painter.SetMainBk(m_sDll,m_nImgeID,m_sType,m_crTrans);
	m_ListBtns.Init(IDB_PAINTER,RGB(239,239,239));
	for (int i = 0; i < _countof(scm_ShapeItems); ++i)
	{
		m_ListBtns.AddButton(&scm_ShapeItems[i]);
	}
	m_ListBtns.SetAssoicatePainter(&m_Painter);
	return TRUE;
}
void CPainterDlg::SetImageParams( LPCTSTR pszDll,LPCTSTR pszType,UINT nImgID ,COLORREF crTrans)
{
	m_sDll = pszDll;
	m_sType = pszType;
	m_nImgeID = nImgID;
	m_crTrans = crTrans;
}
void CPainterDlg::OnColorChanged()
{
	COLORREF cr = m_ColorBar.GetColor();
	m_Painter.SetShapeColor(cr,COLOR_NONE);
}
void CPainterDlg::OnPaint()
{
	CPaintDC dc(this);
	m_Box1.Draw(&dc);
	m_Box2.Draw(&dc);
}
// CPainterList
IMPLEMENT_DYNAMIC(CPainterList, CMFCListCtrl)
CPainterList::CPainterList()
{
	m_pPainter = NULL;
}
CPainterList::~CPainterList()
{
}
BEGIN_MESSAGE_MAP(CPainterList, CMFCListCtrl)
	ON_NOTIFY_REFLECT(NM_CLICK, &CPainterList::OnNMClick)
	ON_NOTIFY_REFLECT(LVN_ITEMCHANGING, &CPainterList::OnLvnItemchanging)
END_MESSAGE_MAP()
void	g_listAutoSizeColumns(CListCtrl&   rList,int   col   /*=-1*/) 
{ 
	//   Call   this   after   your   list   control   is   filled 
	if(!rList.GetHeaderCtrl())return; 
	rList.ShowWindow(SW_HIDE); 
	rList.SetRedraw(FALSE); 
	int   mincol   =   col   <   0   ?   0   :   col; 
	int   maxcol   =   col   <   0   ?   (rList.GetHeaderCtrl()->GetItemCount())-1   :   col; 
	for   (col   =   mincol;   col   <=   maxcol;   col++)   { 
		rList.SetColumnWidth(col,LVSCW_AUTOSIZE); 
		int   wc1   =   rList.GetColumnWidth(col); 
		rList.SetColumnWidth(col,LVSCW_AUTOSIZE_USEHEADER); 
		int   wc2   =   rList.GetColumnWidth(col); 
		int   iFrom=rList.GetTopIndex(); 
		int   nCountPerpage=rList.GetCountPerPage(); 
		if(nCountPerpage <=0) 
			nCountPerpage=rList.GetItemCount(); 
		int   iTo=iFrom+nCountPerpage; 
		int   nImgWidth=0; 
		int   iIndent=0; 
		LVITEM	li; 
		li.mask=LVIF_IMAGE|LVIF_INDENT; 
		IMAGEINFO	ImageInfo; 
		for(int   iItem=iFrom;iItem <iTo;iItem++){ 
			li.iItem=iItem; 
			li.iSubItem=col; 
			rList.GetItem(&li); 
			iIndent=max(li.iIndent,iIndent); 
			if(li.iImage!=-1){ 
				CImageList*	pImageList=rList.GetImageList(LVSIL_SMALL); 
				if(pImageList-> GetSafeHandle()){ 
					pImageList-> GetImageInfo(li.iImage,&ImageInfo); 
					nImgWidth=max(nImgWidth,ImageInfo.rcImage.right-ImageInfo.rcImage.left); 
				} 
			} 
		} 
		int   wc   =   max(20,max(wc1,wc2)); 
		if(col==0) 
			wc+=nImgWidth*(iIndent+1); 
		else 
			wc+=nImgWidth; 
		rList.SetColumnWidth(col,wc); 
	} 
	rList.SetRedraw(TRUE); 
	rList.ShowWindow(SW_SHOW); 
} 
// CPainterList 消息处理程序
void CPainterList::PreSubclassWindow()
{
	CMFCListCtrl::PreSubclassWindow();
	ModifyStyle(0,LVS_SHOWSELALWAYS|LVS_SINGLESEL|LVS_LIST);
}
COLORREF CPainterList::OnGetCellTextColor( int iRow, int iCol )
{
	if (IsSelected(iRow))
		return RGB(255,0,0);
	else
		return RGB(0,0,0);
}
COLORREF CPainterList::OnGetCellBkColor( int iRow, int iCol )
{
	if (IsSelected(iRow))
		return RGB(255,255,225);
	else
		return RGB(255,255,255);
}
BOOL CPainterList::IsSelected( int iRow )
{
	POSITION pos = GetFirstSelectedItemPosition();
	while (pos != NULL)
	{
		if (GetNextSelectedItem(pos) == iRow)
			return TRUE;
	}
	return FALSE;
}
void CPainterList::OnNMClick(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
	// TODO: 在此添加控件通知处理程序代码
	if (NULL == m_pPainter)
		return;
	int iSel = pNMItemActivate->iItem;
	if (-1 == iSel)
	{
		// 没有选中项
		m_pPainter->SetDrawing(SHAPE_NONE);
	}
	else
	{
		DWORD dwData = GetItemData(iSel);
		switch (dwData)
		{
		case SHAPE_LINE:
		case SHAPE_RECT:
		case SHAPE_ELLIPSE:
		case SHAPE_CURVE:
			{
				m_pPainter->SetDrawing((ENUM_SHAPE_TYPE)dwData);
				break;
			}
		case SHAPE_ICON:
			{
				m_pPainter->SetDrawing(ENUM_SHAPE_TYPE(dwData),(LPARAM)m_ImageList.ExtractIcon(7));
				break;
			}
		case ACTION_CLEAR:
			{
				if (YesNoMessageBox(L"清除后不能恢复,确定清除吗?"))
					m_pPainter->ClearDrawed();
				break;
			}
		case ACTION_UNDO:
			{
				m_pPainter->Undo(1);
				break;
			}
		}
	}
	*pResult = 0;
}
void CPainterList::OnLvnItemchanging(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
	// TODO: 在此添加控件通知处理程序代码
	int iSel = pNMLV->iItem;
	if (-1 != iSel)
	{
		if (GetItemData(iSel) > MAX_SHAPE_VALUE)
		{
			*pResult = 1;
			return ;
		}
	}
	*pResult = 0;
}
BOOL CPainterList::Init( UINT nImgeList ,COLORREF crMask)
{
	if (m_ImageList.GetSafeHandle() != NULL)
		return TRUE;
	if (!m_ImageList.Create(24,24,ILC_COLORDDB|ILC_MASK,8,0))
		return FALSE;
	
	CBitmap bmp;
	if (!bmp.LoadBitmap(nImgeList))
		return FALSE;
	m_ImageList.Add(&bmp,crMask);
	SetImageList(&m_ImageList,LVSIL_SMALL);
	
	return TRUE;
}
void CPainterList::AddButton( LPCTSTR sItem,int iImage,DWORD dwData )
{
	int idx = GetItemCount();
	idx = InsertItem(idx,sItem,iImage);
	SetItemData(idx,dwData);
	g_listAutoSizeColumns(*this,0);
}
void CPainterList::AddButton( const _SHAPE_ITEMS *pItem )
{
	AddButton(pItem->sItem,pItem->iImg,pItem->dwData);
}

 

 

----------------------------------------

2011/5/18 更新:

_UpdateDrawing 对于QIcon需要特殊对待,可以提高绘图效率.

void CPainterPane::_UpdateDrawing( CPoint ptMove )
{
	ASSERT(m_pShapeDrawing != NULL);
	// 鼠标按下到上次移动的矩形位置
	CRect rcLast = m_MouseTrack.GetRect();
	// 鼠标按下到此次移动到的矩形位置
	m_MouseTrack.Move(ptMove);
	// 设置图形的矩形为当前的移动的位置
	m_pShapeDrawing->UpdateEndPoint(ptMove);
	// 当前图像的矩形
	CRect rcDrawing = m_pShapeDrawing->GetRect();
	// 取两个矩形的并集进行更新,它们的并集就是当前的脏矩形
	BOOL bOK = TRUE;
	if (SHAPE_ICON != m_eDrawing)
	{
		if(bOK = rcDrawing.UnionRect(&rcDrawing,&rcLast))
		{
			// 边界增大一点是为了不使图形切边
			rcDrawing.InflateRect(1,1);
		}
	}
	if (bOK)
	{
		// 先拷贝需要更新区域的已画好的图形到正在画的区域中
		CopyDC(&m_dcMemDrawing,&m_dcMemDrawed,rcDrawing);
		// 画图形
		m_pShapeDrawing->Draw(&m_dcMemDrawing);
		// 更新到显示dc,
		// 只有当鼠标左键弹起,标志这个图形画好的时候才能更新到m_dcMemDrawed 上
		CopyDCToSurface(&m_dcMemDrawing,rcDrawing);
	}
}



 

同时: QIcon::Draw是错误的.因为DrawIcon绘制出的图标大小是GetSystemMetrics(SM_CXICON)GetSystemMetrics(SM_CYICON)得到的大小,可以使用DrawIconEx代替之.

void QIcon::Draw( CDC *pDC )
{
	if (NULL != m_hIcon)
	{
		CRect rc = GetRect();
		DrawIconEx(pDC->GetSafeHdc(),m_pt1.x,m_pt1.y,m_hIcon,rc.Width(),
			rc.Height(),0,NULL,DI_NORMAL);
	}
}


 

同时: QIcon::Draw是错误的.因为DrawIcon绘制出的图标大小是GetSystemMetrics(SM_CXICON)GetSystemMetrics(SM_CYICON)得到的大小,可以使用DrawIconEx代替之.

原文地址:https://www.cnblogs.com/SunkingYang/p/11049260.html