zoukankan      html  css  js  c++  java
  • 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代替之.

  • 相关阅读:
    PrintWriter write与println方法的区别
    java线程之一 单线程
    Android系列之ListView实现分页和类似异步加载效果(转载)
    Failed to fetch URL http://dlssl.google.com/android/repository/addons_list
    java rpc 综述(上)
    横竖屏切换时候Activity的生命周期
    【转载】C# 大数相乘
    ASP.NET 2.0 中使用PreviousPage的强类型属性
    人生一世
    SQL 中的indexof函数CHARINDEX
  • 原文地址:https://www.cnblogs.com/SunkingYang/p/11049260.html
Copyright © 2011-2022 走看看