VC学习笔记:简单绘图

VC学习笔记:简单绘图

SkySeraph Oct.29th 2009  HQU

Email-zgzhaobo@gmail.com  QQ-452728574

Latest Modified Date:Oct.31th 2010 HQU 重新整理

//说明:学习《孙鑫视频》第四课 笔记  2009.10.29HQU

绘线及相关

建立单文档工程,为View类(不能是MainFrame)添加消息函数OnLButtonDown、OnLButtonUp

①先增加成员变量m_ptOrigin,CPoint型,用来保存坐标原点,在View构造函数里把其赋值为0,在OnLButtonDown里m_pOrigin=point

CMyView::OnLButtonUp(UINT nFlags,CPoint point)

//法一:利用SDK全局函数/API函数  在View类OnLButtonUp中

HDC hdc;//获取设备描述表

hdc=::GetDC(m_hWnd);

//利用全局函数而不是CWnd成员函数;m_hWnd是view窗口句柄,在CWnd派生出来的类中,都有一个数据成员保存了窗口句柄,

//CDC类也有同样的功能 m_hWnd是一个保护的变量,是用来构造m_hWnd对象的CWnd指针的HWND。

MoveToEx(hdc,m_ptOrigin.x,m_ptOrigin.y,NULL);//移动线条起点

LineTo(hdc,point.x,point.y);//画线

::ReleaseDC(m_hWnd,hdc);//配对使用;在WM_PAINT中不能用

 

//法二:利用CDC类  此类提供了一个数据成员m_hDC用来保存于CDC类相关的句柄,类似于m_hWnd

CDC *pDC=GetDC();//定义CDC类指针

pDC->MoveTo(m_ptOrigin);

pDC->LineTo(point);

ReleaseDC(pDC);

 

//法三:利用CClientDC  ,创建的是视图窗口的DC对象,作图只能在视图的客服区内作图

CClientDC dc(GetParent());  //=CClientDC dc(this);    //结束时会自释放DC

//CClientDC构造函数CClientDC( CWnd* pWnd );中得知需要一个指针

//类CClientDC派生于CDC,在构造时调用了Windows函数GetDC,在析构时调用了ReleaseDC。这意味着和CClientDC对象相关的设备上下文是窗口的客户区。

dc.MoveTo(m_ptOrigin);  //CClientDC类型的变量dc是一个对象,采用点操作符来调用该对象的函数

dc.LineTo(point);

 

//法四:利用CWindowDC   //通过选择窗口对象的指针,可以创建各种窗口的DC甚至是整个屏幕窗口的DC对象,作图也只能在窗口的客服区内作图

CWindowDC dc(GetDesktopWindow());//整个屏幕

//CWindowDC dc(GetParent());//=CWindowDC dc(this); //视图客户区内作图,结束时会自释放DC

//CWindowDC类是从CDC继承的。它在构造的时候调用Windows函数GetWindowDC,在销毁的时候调用ReleaseDC。

dc.MoveTo(m_ptOrigin);

dc.LineTo(point);

 

//绘制彩色线条    使用画笔 可以选择/改变颜色,粗细,宽度等属性

CPen pen(PS_DOT,1,RGB(0,255,0));

CClientDC dc(this);

CPen *pOldPen=dc.SelectObject(&pen);

dc.MoveTo(m_ptOrigin);

dc.LineTo(point);

dc.SelectObject(pOldPen);

 

CView::OnLButtonUp(nFlags,point);

}

 

画刷

法一 简单画刷

CBrush brush(RGB(255,0,0));  

CClientDC dc(this);

dc.FillRect(CRect(m_ptOrigin,point),&brush);

//注:此处只是用指定的画刷填充一块区域,因此并不需要把话刷选入设备描述表中。设备描述表中存在一个默认的白色的画刷

法二bitmap填充/位图画刷

CBrush brush(RGB(255,0,0));

CBitmap bitmap;

bitmap.LoadBitmap(IDB_BITMAP1);

CBrush brush(&bitmap);

 

法三:透明画刷

CClientDC dc(this);

CBrush *pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH)); // CBrush::FromHandle是静态成员函数  

//FromHandle给出一个Windows HBRUSH对象句柄时,返回一个指向CBrush对象的指针。即将画刷的句柄转换为对象的指针。

CBrush *pOldBrush=dc.SelectObject(pBrush);

dc.Rectangle(CRect(m_ptOrigin,point));

dc.SelectObject(pOldBrush);

静态成员 原理说明 实例

  • 例子1.0

#include "iostream"

class Point

{

public:

void output() {}

static void init() {}  //静态函数,不属于某个具体的对象,即在未产生Point任何对象时,这个类已经存在于程序的代码区

}

 

void main()

{

/*法一

Point pt; //构造对象

pt.init();

pt.output();

*/

//法二

Point::init();/

Point::output();//错误

}

说明:法一正确;法二执行错误,原因:

//静态成员函数和静态成员变量属于类本身,在类加载的时候,即为它们分配了空间,故可以用类名::函数名或类名:变量名来访问;

//而非静态成员函数和非静态成员属于对象的方法和数据,也就是应该先产生类的对象,然后通过类的对象去引用。

  • 修改1.1:

class Point

{

public:

void output() {}

static void init() {x=0;y=0;}

private:

int x,y;

}

void main()

{ Point::init();//错误:在静态成员函数中不能调用非静态成员

 }

说明:在静态成员函数中不能调用非静态成员(静态成员函数和静态成员变量);反之在非静态成员函数中可以调用静态成员,可以在修改void output(){ init(); };检验

内存模型:无论采取什么样的操作,程序代码都是在内存中执行的,只有在内存中占有一席之地,我们才能访问它。

 

 

  • 修改1.2

在int x,y;前加static,编译无错,链接时出错

说明:对于静态成员变量,必须对其进行初始化,且必须在类外进行此操作

加上:int Point::x=0;int Point::y=0;   OK!

任意画线,画扇形

①先增加消息处理函数OnMouseMove。

②再设定一个bool型变量(当鼠标左键按下为true,弹起为false):在View构造函数里m_bDraw=0,在OnLButtonDown里m_bDraw=TRUE&在OnLButtonUp里m_bDraw=FALSE

③任意画连续的线

CClientDC dc(this); if(m_bDraw==TRUE)

{  dc.MoveTo(m_ptOrigin);  dc.LineTo(point);  m_ptOld=point; }

④增加颜色的连续的线

CPen pen(PS_SOLID,1,RGB(255,0,0));

CPen *pOldPen=dc.SelectObject(&pen);   {……}

dc.SelectObject(pOldPen);

⑤画扇形

先增加一个CPoint型成员变量m_ptOld,在左键按下的时候赋其值给m_ptOld

…{dc.MoveTo(m_ptOrigin);

dc.LineTo(m_ptOld);

dc.MoveTo(m_ptOrigin);

dc.LineTo(point);

m_ptOld=point; }…

⑥带边线的扇形:只需要把第三句dc.MoveTo(m_ptOrigin);改为dc.MoveTo(m_ptOld);既可。

⑦设置绘图模式的函数:dc.SetROP2(R2_BLACK);

补充 知识点

  1. CWnd类定义了一个HWND类型的成员变量m_hWnd用于保存当前窗口的句柄,且该句柄具有public类型访问权限。由继承性原理,CWnd子类都拥有这一成员变量。
  1. 在单文档中view挡在MainFrame的前面。此时如果编写针对MainFrame的mouseClick事件,将不会有反应。

在框架类当中收不到鼠标左键点击的消息原因:View类始终是覆盖在Frame类中上的。也就是说,所有操作,包括鼠标移动,鼠标单击等都只能由视图类来完成。

 

CDC、HDC、pDC区别

① CDC *pDC 和 HDC hdc有什么不同, 类似的有CWnd *pWnd和HWnd?

 pDC是类指针,HDC是windows句柄  

通过pDC获得hdc:  HDC hdc=pDC->GetSafeHdc();

通过hdc获得pDC:  CDC *pDC=new CDC;  pDC->Attach(hdc); 

②hDC和CDC有本质区别 

HDC是WINDOWS的一种数据类型,是设备描述句柄。而CDC是MFC里的一个类,它封装了几乎所有的关于HDC的操作。

也可以这样说,HDC定义的变量指向一块内存,这内存用来描述一个设备的相关的内容,所以也可以认为HDC定义的是一个指针;

而CDC类定义一个对象,这个对象拥有HDC定义的一个设备描述表,同时也包含与HDC相关的操作的函数。这与HPEN和CPen,POINT与CPoint之间的差别是一样的。

CDC是对hDC的相关操作进行封装,例如CDC的一个TextOut函数隐去其错误检测,完全可以简化到这样程度CDC:TextOut( int x, int y, const CString& str )

{

    TextOut( m_hDC, x, y, (LPCTSTR)str, str.GetLength() );

m_hDC就是CDC的成员变量HDC m_hDC;

CDC有一个operator HDC() const { return m_hDC; }  

你可以把它当成一个HDC使用

③ this是dc输出目标窗口的指针,通过它可以得到窗口句柄,对象带参构造这有什么奇怪的呢?       

    CPaintDC        无效区dc,相当于BeginPaint,    EndPaint  

    CClientDC       客户区dc,相当于GetDC,         ReleaseDC  

    CWindowDC       整窗口dc, 相当于GetWindowDC,   ReleaseDC  

    CDC             任何dc,   相当于CreateDC,      DeleteDC

HDC就是最原始的 DC 句柄,很多API的第一个参数就是一个HDC类型,比如

HDC hDC = ::GetDC( m_hWnd);

::MoveToEx( hDC, 0,0, NULL );   

::LineTo( hDC, 0, 100, );

::ReleaseDC( m_hWnd, hDC );

在MFC中,为了将API封装成一个类来操作,因此多出来了一个CDC。所以在MFC中,都是

CDC dc = GetDC();

dc.MoveTo( 0,0 );

dc.LineTo( 0,100 );

this->ReleaseDC( &dc );

但这样还不够,因为 CDC还要你自己去释放,所有MFC中又多出来一个CClientDC, 这样你就可以这样了:

CClientDC dc(this);

dc.MoveTo( 0,0 );

dc.LineTo( 0,100 );

CClientDC的析构函数自己会释放自己。

DC不是什么对象,就是设备上下文的简称。与CClientDC一样,还有CWindowDC,CPaintDC,只是它们的绘制范围不一样。 

但弄到底,都只是HDC的一些封装而已,你可以在CDC类中直接引用 m_hDC,这就是那个原始的HDC句柄了。

Author:         SKySeraph

Email/GTalk: zgzhaobo@gmail.com    QQ:452728574

From:         http://www.cnblogs.com/skyseraph/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,请尊重作者的劳动成果。

原文地址:https://www.cnblogs.com/skyseraph/p/1865748.html