葡京网投哪个正规 > 首页 > 让CDC输出的图形具有保持功能的三种方法,VC视频教程笔记

原标题:让CDC输出的图形具有保持功能的三种方法,VC视频教程笔记

浏览次数:178 时间:2020-03-25

第11课
1.创建4个菜单,为其添加消息响应,用成员变量保存绘画类型。添加LButtonDown和Up消息。
2.当窗口重绘时,如果想再显示原先画的数据,则需要保存数据。为此创建一个新类来记录绘画类型和两个点。
class CGraph  
{
public:
CPoint m_ptOrigin;//起点
CPoint m_ptEnd;//终点
UINT m_nDrawType;//绘画类型
CGraph();
CGraph(UINT m_nDrawType,CPoint m_ptOrigin,CPoint m_ptEnd);//此为构造函数。
virtual ~CGraph();

  看了孙鑫老师的书后,顺便对已绘制的图形具有保持功能作一下总结。总的来说,有三种方法可以图形具体保持功能。分别如下:

ClientToScreen( )是把窗口坐标转换为屏幕坐标
葡京网投哪个正规 1ScreenToClient( )是把屏幕坐标转换为窗口坐标
葡京网投哪个正规 2屏幕坐标是相对于屏幕左上角的,而窗口坐标是相对于窗口用户区左上角的
葡京网投哪个正规 3VC下,有些函数使用窗口坐标,有些使用屏幕坐标,使用时要分清。
葡京网投哪个正规 4
葡京网投哪个正规 5
葡京网投哪个正规 6一个窗体分为两部分:系统区和客户区
葡京网投哪个正规 7象标题和菜单之类的是系统区,由系统来控制,客户区就是你的地盘喽!!!
葡京网投哪个正规 8Width, Height 是指整体的,ClientWidth, ClientHeight是指客户区的,两者相减就是
葡京网投哪个正规 9系统区的啦!!!
葡京网投哪个正规 10ClientToScreen是把坐标从当前窗体转化成全屏幕的!!!
葡京网投哪个正规 11ScreenToClient是把屏幕坐标转化成相对当前窗体的坐标!!!!
葡京网投哪个正规 12
葡京网投哪个正规 13bool  m_bIsLButtonDawn =false;
葡京网投哪个正规 14
葡京网投哪个正规 15void CDrawDlg::OnMouseMove(UINT nFlags, CPoint point) 
葡京网投哪个正规 16葡京网投哪个正规 17葡京网投哪个正规 18{
葡京网投哪个正规 19 // TODO: Add your message handler code here and/or call default
葡京网投哪个正规 20 CWnd *pwnd=GetDlgItem(IDC_EDIT1);
葡京网投哪个正规 21    CDC *pdc=pwnd->GetDC();
葡京网投哪个正规 22 CRect rect;
葡京网投哪个正规 23 this->ClientToScreen(&point);
葡京网投哪个正规 24 pwnd->ScreenToClient(&point);
葡京网投哪个正规 25 pwnd->GetClientRect(&rect);
葡京网投哪个正规 26
葡京网投哪个正规 27//  HCURSOR hcur=::LoadCursorFromFile("pen.cur"); 
葡京网投哪个正规 28 //  SetClassLong(GetSafeHwnd(),GCL_HCURSOR,(LONG)hcur);  
葡京网投哪个正规 29
葡京网投哪个正规 30葡京网投哪个正规,// CPen pen(PS_INSIDEFRAME,-1,RGB(255,255,255));
葡京网投哪个正规 31//     CPen* olePen=pdc->SelectObject(&pen);
葡京网投哪个正规 32 if(rect.PtInRect(point) &&  m_bIsLButtonDawn )
葡京网投哪个正规 33葡京网投哪个正规 34 葡京网投哪个正规 35{
葡京网投哪个正规 36
葡京网投哪个正规 37  pdc->DPtoLP(&m_fp);
葡京网投哪个正规 38  pdc->MoveTo(m_fp);
葡京网投哪个正规 39  pdc->DPtoLP(&point);
葡京网投哪个正规 40葡京正网网投,  pdc->LineTo(point);
葡京网投哪个正规 41
葡京网投哪个正规 42 }
葡京网投哪个正规 43  m_fp=point;
葡京网投哪个正规 44//  pdc->SelectObject(olePen);
葡京网投哪个正规 45 ReleaseDC(pdc);
葡京网投哪个正规 46 CDialog::OnMouseMove(nFlags, point);
葡京网投哪个正规 47}
葡京网投哪个正规 48
葡京网投哪个正规 49void CDrawDlg::OnLButtonUp(UINT nFlags, CPoint point) 
葡京网投哪个正规 50葡京网投哪个正规 51葡京网投哪个正规 52{
葡京网投哪个正规 53  m_bIsLButtonDawn =false;
葡京网投哪个正规 54 // TODO: Add your message handler code here and/or call default
葡京网投哪个正规 55葡京网投哪个正规 56/**//*
葡京网投哪个正规 57   CWnd *pwnd=GetDlgItem(IDC_EDIT1);
葡京网投哪个正规 58     CDC *pdc=pwnd->GetDC();
葡京网投哪个正规 59  CRect rect;
葡京网投哪个正规 60  this->ClientToScreen(&point);
葡京网投哪个正规 61  pwnd->ScreenToClient(&point);
葡京网投哪个正规 62  pwnd->GetClientRect(&rect);
葡京网投哪个正规 63  
葡京网投哪个正规 64  if(rect.PtInRect(point))
葡京网投哪个正规 65  {
葡京网投哪个正规 66   pdc->DPtoLP(&m_fp);
葡京网投哪个正规 67   pdc->MoveTo(m_fp);
葡京网投哪个正规 68   pdc->DPtoLP(&point);
葡京网投哪个正规 69   pdc->LineTo(point);
葡京网投哪个正规 70 
葡京网投哪个正规 71  }
葡京网投哪个正规 72  ReleaseDC(pdc);*/
葡京网投哪个正规 73 
葡京网投哪个正规 74 CDialog::OnLButtonUp(nFlags, point);
葡京网投哪个正规 75}
葡京网投哪个正规 76
葡京网投哪个正规 77void CDrawDlg::OnLButtonDown(UINT nFlags, CPoint point) 
葡京网投哪个正规 78葡京网投哪个正规 79葡京网投哪个正规 80{
葡京网投哪个正规 81 // TODO: Add your message handler code here and/or call default
葡京网投哪个正规 82 CWnd *pwnd=GetDlgItem(IDC_EDIT1);
葡京网投哪个正规 83 CDC *pDC=pwnd->GetDC();
葡京网投哪个正规 84 CRect rect;
葡京网投哪个正规 85 this->ClientToScreen(&point);
葡京网投哪个正规 86 pwnd->ScreenToClient(&point);
葡京网投哪个正规 87 pwnd->GetClientRect(&rect);
葡京网投哪个正规 88 if(rect.PtInRect(point))
葡京网投哪个正规 89葡京网投哪个正规 90 葡京网投哪个正规 91{
葡京网投哪个正规 92  m_fp.x=point.x;
葡京网投哪个正规 93  m_fp.y=point.y;
葡京网投哪个正规 94 }
葡京网投哪个正规 95 ReleaseDC(pDC);
葡京网投哪个正规 96  m_bIsLButtonDawn =true;
葡京网投哪个正规 97 CDialog::OnLButtonDown(nFlags, point);
葡京网投哪个正规 98}

  继续前二篇的话题,让CDC输出图形具有保持功能的第三种方法是利用兼容DC。

};
     然后在void CGraphicView::OnLButtonUp(UINT nFlags, CPoint point)中加入如下代码
//CGraph graph(m_nDrawType,m_ptOrigin,point);//不能用局部变量
//m_ptrArray.Add(&graph);//加入这种指针数组中
/* OnPrepareDC(&dc);//这个函数中可以重新设置窗口原点,对于滚动条中,保存数据前要调用此函数
dc.DPtoLP(&m_ptOrigin);//将设备坐标转换为逻辑坐标
dc.DPtoLP(&point);//
CGraph *pGraph=new CGraph(m_nDrawType,m_ptOrigin,point);//在堆中创建新的对象
m_ptrArray.Add(pGraph);*///加入到指针数组中
在GraphicView.h中有如下代码
CPtrArray m_ptrArray;
     在OnDraw中重画时调出数据
for(int i=0;i<m_ptrArray.GetSize();i++)
3.在CView::OnPaint()调用了OnDraw(),但在void CGraphicView::OnPaint()中MFC的Wizard没有调用OnDraw(),要注意这个区别。如果你此时想调用,必须手动添加代码。 OnDraw(&dc);
4.让窗口具有滚动条的功能。
     第1.将CGraphicView的头文件中的CView全部替换成CSrollView
     第2.添加如下的代码
void CGraphicView::OnInitialUpdate()
{
CScrollView::OnInitialUpdate();

  1. 在每次绘制图形后,用一个对象数组来保存已经绘制的样式以及图形坐标 。只在在窗体重绘时重新来绘制这些保存的图形数据即可。
  2. 采用元数据文件,它采用了元数据文件设备上下文来保存已绘制的图形,每次窗体重绘时再播放元数据文件来实现图形保持功能。
  3. 这种方法采用一个兼容DC,它利用一个兼容位图(相当于一块画布),用户在所有绘制图形操作都在这一块画布上进行,这块画布同时也保存了用户的所有绘制操作,当窗体时行重绘时,当前窗口重绘DC把已绘制好图形的画布直接拷贝到当前DC中,这样就达到了图形保持功能。

  

// TOD Add your specialized code here and/or call the base class
SetScrollSizes(MM_TEXT,CSize(800,600));//设置映射模式,设定窗口大小。OK!
}
5.坐标系的转换,此处不再详细介绍,需要时请查阅相关资料。
6.解决重绘时线跑到上面的问题。为什么会错位?因为逻辑坐标和设备坐标没有对应起来。
解决方法:
    在OnLButtonDown画完图后,保存之前。调用
/* OnPrepareDC(&dc);//重新设置逻辑坐标的原点!!!
dc.DPtoLP(&m_ptOrigin);//设备坐标转化为逻辑坐标
dc.DPtoLP(&point);
CGraph *pGraph=new CGraph(m_nDrawType,m_ptOrigin,point);
m_ptrArray.Add(pGraph);*/
7.另外两种方法来保存数据。
    一种是用CMetaFileDC
    另一种是利用兼容DC,重绘时利用 pDC->BitBlt(0,0,rect.Width(),rect.Height(),&m_dcCompatible,0,0,SRCCOPY);
将兼容DC的图拷贝到屏幕DC上去。
此处不再详细介绍这两种方法,因为介绍多了容易搞晕。呵呵

  分别就这三情况,我给出了参考代码,以后仅作笔记使用。

思路首先利用当前绘制DC来创建一个兼容DC,创建完成之后

  第一种最普通的方法:

兼容DC相当于一个与它关联的DC的引用,好比一块画布,用户在这块画布上作图。当窗体重绘时,直接把这块画布上图形拷贝到当前DC,这样就达到了保持图形的功能。刚刚说到 一块画布,其实兼容DC采用一块和当前客户区域同样大小的位图作为画布。

    

 

第一种:保存重绘图形数据

   具体代码如下:

//自定义一个类,提供重绘图形数据
class CGraph  
{
public:
    CGraph();
    CGraph(UINT m_nDrawType,CPoint m_ptOrigin,CPoint m_ptEnd);
    virtual ~CGraph();
public:
    UINT m_nDrawType;  //绘制类型
    CPoint m_ptOrigin; //图形原点
    CPoint m_ptEnd;    //图形终点
}; 

void CGraphicView::OnLButtonUp(UINT nFlags, CPoint point) 
{
    // TODO: Add your message handler code here and/or call default
    CClientDC dc(this);
    CBrush *pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
    dc.SelectObject(pBrush);
    
        //如果已经创建兼容DC,则不再创建
    if(!m_dcCompatible.m_hDC)
    {
        m_dcCompatible.CreateCompatibleDC(&dc);
        CRect rect;
        GetClientRect(&rect);
        CBitmap bitmap;、
                //构建一个和当前客户区大小一样的位图
        bitmap.CreateCompatibleBitmap(&dc,rect.Width(),rect.Height());
        m_dcCompatible.SelectObject(&bitmap);
        m_dcCompatible.BitBlt(0,0,rect.Width(),rect.Height(),&dc,0,0,SRCCOPY);
        m_dcCompatible.SelectObject(pBrush);
    }
    switch(m_nDrawType)
    {
    case 1:
        m_dcCompatible.SetPixel(point,RGB(0,0,0));
        break;
    case 2:
        m_dcCompatible.MoveTo(m_ptOrigin);
        m_dcCompatible.LineTo(point);
        break;
    case 3:
        m_dcCompatible.Rectangle(CRect(m_ptOrigin,point));
        break;
    case 4:
        m_dcCompatible.Ellipse(CRect(m_ptOrigin,point));
        break;
    }
    CScrollView::OnLButtonUp(nFlags, point);
}

接下来,在每次绘制图形后保存需要该绘制图形数据

   当窗口需要重绘时,重新得用兼容DC来构建图形,代码如下:

void CGraphicView::OnLButtonUp(UINT nFlags, CPoint point) 
{
    // TODO: Add your message handler code here and/or call default
    CClientDC dc(this);
    CBrush *ptBrush = CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
    dc.SelectObject(ptBrush);
        //绘制图形
    switch(m_nDrawType)
    {
        case 1:
            dc.SetPixel(point,RGB(0,0,0));
            break;
        case 2:
            dc.MoveTo(m_ptOrigin);
            break;
        case 3:
            dc.Rectangle(CRect(m_ptOrigin,point));
            break;
        case 4:
            dc.Ellipse(CRect(m_ptOrigin,point));
            break;
    }

void CGraphicView::OnDraw(CDC* pDC)
{
    CGraphicDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    
    CRect rect;
    GetClientRect(&rect); 
        //使用拷贝的方式来重新绘制图形
    pDC->BitBlt(0,0,rect.Width(),rect.Height(),&m_dcCompatible,0,0,SRCCOPY);
}

    //保存图形数据,保存之前,先将设置点转换为逻辑点
    OnPrepareDC(&dc);
    dc.DPtoLP(&m_ptOrigin);
    dc.DPtoLP(&point); 
        //在堆中分配一块空间来保存重绘图形数据
    CGraph *g = new CGraph(m_nDrawType,m_ptOrigin,point);    //must use a point to CGraph
        //m_ptArray类型是一个CPtrArray类型的成员变量
    m_ptrArray.Add(g);
    
    CScrollView::OnLButtonUp(nFlags, point);
}

  这种方式比较简单,且通俗易懂。

最后一步就是窗口重绘图时重新绘制这些图形即可。

  这三个方法总算写完了,算是对自己学习的一种总结吧!

 

 

//OnDraw函数在调用之前,会先调用OnPrepareDC将逻辑点转换为设备点
void CGraphicView::OnDraw(CDC* pDC)
{
    CGraphicDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);

 

    //redraw
    CClientDC dc(this);
    CBrush *brush = CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
    dc.SelectObject(brush);
    for(int i=0; i<m_ptrArray.GetSize(); i++)
    {
        switch(((CGraph*)m_ptrArray.GetAt(i))->m_nDrawType)
        {
        case 1:
            dc.SetPixel(((CGraph*)m_ptrArray.GetAt(i))->m_ptEnd,RGB(0,0,0));
            break;
        case 2:
            dc.MoveTo(((CGraph*)m_ptrArray.GetAt(i))->m_ptOrigin);
            dc.LineTo(((CGraph*)m_ptrArray.GetAt(i))->m_ptEnd);
            break;
        case 3:
            dc.Rectangle(CRect(((CGraph*)m_ptrArray.GetAt(i))->m_ptOrigin,
                ((CGraph*)m_ptrArray.GetAt(i))->m_ptEnd));
            break;
        case 4:
            dc.Ellipse(CRect(((CGraph*)m_ptrArray.GetAt(i))->m_ptOrigin,
                ((CGraph*)m_ptrArray.GetAt(i))->m_ptEnd));
            break;
        }
    }

}

  这是一种常规做法,代码量比其它二种要稍多一些。其它二种方法,另写二篇吧!算凑个数吧,嘻嘻!

  有关坐标点的转换问题,其实很简单,不用想得太复杂,其实就是二种坐标点的转换问题,逻辑点转换为设备点以及设备点转换为逻辑点,平时我们用到的绘图函数用到的坐标都是逻辑点。如果要输出到设备上(显示器,打印机)都得转换为设备点。设备点的原点永远都是客户区的(0,0)坐标点。我们只需要根据公式做映射即OK了。其实MFC已经帮我们做好了映射了,每次在响应WM_PAINT的事件中,都会去调用 OnPrepareDC(CDC* dc)方法 ,  在这个方法中,就做了转换坐标的方法。

 

  多看看MFC的代码,一切原理都会变得不是那么神秘!

本文由葡京网投哪个正规发布于首页,转载请注明出处:让CDC输出的图形具有保持功能的三种方法,VC视频教程笔记

关键词:

上一篇:编译方面错误,VC6涉及到的文件扩展名

下一篇:VC小知识总结【葡京正网网投】,MFC自绘控件学习总结第二贴