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);
补充 知识点
- CWnd类定义了一个HWND类型的成员变量m_hWnd用于保存当前窗口的句柄,且该句柄具有public类型访问权限。由继承性原理,CWnd子类都拥有这一成员变量。
- 在单文档中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/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,请尊重作者的劳动成果。