vc 画图控件

    技术2022-05-20  63

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

    项目的需要,要实现一个画图的功能,在网上搜了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); } } 


    最新回复(0)