MFC 绘图

目录

MFC中绘图

CPaintDC,封装了在WM_PAINT消息中绘图的绘图设备

CClientDC类,封装了在客户区绘图的绘图设备

CGdiObject类(绘图对象类),封装了各种绘图对象相关的操作


MFC中绘图

Windows绘图需要绘图设备,Win32:绘图设备句柄(HDC);MFC:绘图类对象,本质上还是类绑定句柄。

绘图相关类:

CDC类(绘图设备类):封装了各种绘图相关的函数,以及两个非常重要的的成员变量m_hDC和m_hAttribDC。有以下两个子类:

  • CPaintDC类,封装了在WM_PAINT消息中绘图的绘图设备
  • CClientDC类,封装了在客户区绘图的绘图设备

CGdiObject类(绘图对象类):封装了各种绘图对象相关的操作,以及一个非常重要的成员变量m_hObject(绘图对象句柄),有以下几个子类:

  • CPen类,封装了画笔的操作
  • CBrush类,封装了画刷的操作
  • CFont类,封装了字体的操作
  • CBitmap类,封装了位图的操作

CPaintDC,封装了在WM_PAINT消息中绘图的绘图设备

创建一个单文档视图架构的项目,视图类管理视图窗口,是现实图像用的,应该在视图类中使用绘图类。处理WM_PAINT消息,类向导如下:

处理WM_PAINT消息

cpp 复制代码
void CMFCDrawView::OnPaint()
{
	CPaintDC dc(this); //::BeginPaint
//	dc.Rectangle( 100, 100, 300, 300 );//::Rectangle(...)
//	::Rectangle( dc.m_hDC, 100, 100, 300, 300 );
}

绘图类谁也代表不了,无法代表绘图设备,只有和绘图设备句柄绑定才行。

下断点,看看CPaintDC的构造函数,参数是视图类对象地址,函数内部this是CPaintDC对象地址

函数伪代码如下:

cpp 复制代码
CPaintDC dc(pView) === CPaintDC::CPaintDC(...)//函数内部this为&dc
{
  Attach(::BeginPaint(pView->m_hWnd, &m_ps))//函数内部this为&dc
  {
    m_hDC = hDC;//将BeginPaint获取的绘图设备句柄  保存到dc对象的一个成员变量中
    SetAttribDC(m_hDC)//函数内部this为&dc
    {
      m_hAttribDC = m_hDC;//dc对象的另一个成员变量也保存了绘图设备句柄
    }
  }
}

所以m_hDC和m_hAttribDC保存的是绘图设备句柄,本质上依然是Windows API

cpp 复制代码
_AFXWIN_INLINE BOOL CDC::Rectangle(int x1, int y1, int x2, int y2)
	{ ASSERT(m_hDC != NULL); return ::Rectangle(m_hDC, x1, y1, x2, y2); }

等价于

cpp 复制代码
::rectangle( dc.m_hdc, 100, 100, 300, 300 );

在Win API 编程,使用绘图设备,需要有拿到绘图设备句柄和释放。在MFC中这个任务就交给了构造函数和析构函数。

cpp 复制代码
CPaintDC::CPaintDC(CWnd* pWnd)
{
	ASSERT_VALID(pWnd);
	ASSERT(::IsWindow(pWnd->m_hWnd));

	if (!Attach(::BeginPaint(m_hWnd = pWnd->m_hWnd, &m_ps)))   // 获取绘图设备句柄
		AfxThrowResourceException();
}

CPaintDC::~CPaintDC()
{
	ASSERT(m_hDC != NULL);
	ASSERT(::IsWindow(m_hWnd));

	::EndPaint(m_hWnd, &m_ps);          // 释放绘图设备句柄
	Detach();
}

代码如下:

cpp 复制代码
void CMFCDrawView::OnPaint()
{
	CPaintDC dc(this); // device context for painting
					   // TODO: 在此处添加消息处理程序代码
					   // 不为绘图消息调用 CView::OnPaint()
	dc.Rectangle(100, 100, 300, 300);
	//::Rectangle(dc.m_hDC, 100, 100, 300, 300);
}

结果如下:

CClientDC类,封装了在客户区绘图的绘图设备

给菜单新加一项,ID为 ID_Client

使用类向导处理点击后的WM_COMMAND消息,四个框架类都可以处理,因为是在视图窗口绘图,所以在视图窗口处理。

生成函数,函数内部this是视图窗口对象地址,用于在视图窗口的客户区的窗口绘图

结果如下:

但是当窗口放大缩小后,圆形消失不见了,但是矩形还在。

原因:触发WM_PAINT消息后,OnPaint函数就会被触发执行,所以矩形一致都在,而OnClient则不是。

**CGdiObject类(绘图对象类),**封装了各种绘图对象相关的操作

拿画笔举例子:

  1. 创建一根画笔
  2. 把画笔给绘图设备
  3. 使用画笔画画
  4. 要回画笔
  5. 销毁画笔

创建如下4个菜单项

创建处理WM_COMMAND消息的函数

画笔:

cpp 复制代码
void CMFCDrawView::OnPen()
{
	CClientDC dc( this );
	CPen pen(PS_SOLID, 2, RGB(255,0,0));  // 实心 像素为2 颜色为红色

	CPen* oldpen = dc.SelectObject( &pen );
	//HGDIOBJ nOldPen = ::SelectObject( dc.m_hDC, pen.m_hObject );

	dc.Rectangle( 100, 100, 300, 300 );
	//::Rectangle( dc.m_hDC, 100, 100, 300, 300 );

	dc.SelectObject( oldpen );
	//::SelectObject( dc.m_hDC, nOldPen );

	pen.DeleteObject( );
	//::DeleteObject( pen.m_hObject );
}

跟下CPen的构造函数,伪代码如下:其它也是同理

cpp 复制代码
CPen pen(PS_SOLID, 2, RGB(255,0,0)) === CPen::CPen(PS_SOLID, 2, RGB(255,0,0))
//函数内部this &pen
{
  Attach(::CreatePen(PS_SOLID, 2, RGB(255,0,0)))//函数内部this &pen
  {
    m_hObject = hObject;//将::CreatePen获取的画笔句柄,保存到pen对象的一个成员变量中
  }
}

画刷:

cpp 复制代码
void CMFCDrawView::OnBrush()
{
	CClientDC dc(this);
	CBrush brush(RGB(0,255,0));
	CBrush* oldbrush = dc.SelectObject( &brush );
	dc.Rectangle( 100, 100, 300, 300 );
	dc.SelectObject( oldbrush );
	brush.DeleteObject( );
}

字体

cpp 复制代码
void CMFCDrawView::OnFont()
{
	CClientDC dc( this );
	CFont font;
	font.CreatePointFont(300, "黑体");//::CreateFont(..............)
	CFont* oldfont = dc.SelectObject( &font );
	dc.TextOut( 100, 100, "hello");
	dc.SelectObject( oldfont );
	font.DeleteObject();
}

位图:

  1. 添加位图资源
  2. 创建一个和当前DC相匹配的内存DC
  3. 将位图的数据给内存DC
  4. 成像
  5. 将位图数据要回来
  6. 销毁位图
  7. 销毁内存DC
cpp 复制代码
void CMFCDrawView::OnBmp()
{
	//添加位图资源(操作资源无需写代码)

	//创建一个和当前DC,相匹配的内存DC
	CClientDC dc( this );
	CDC memdc;
	memdc.CreateCompatibleDC( &dc ); //::CreateCompatibleDC
	//将位图的数据送给内存DC
	CBitmap bmp;
	bmp.LoadBitmap( IDB_BITMAP1 ); //::LoadBitmap


	CBitmap* oldbmp = memdc.SelectObject( &bmp );//::SelectObject
	//成像
	dc.BitBlt( 100, 100, 48, 48, &memdc, 0, 0, SRCCOPY );//::BitBlt
	//将位图数据要回来
	memdc.SelectObject( oldbmp );//::SelectObject
	//销毁位图
	bmp.DeleteObject( );//::DeleteObject
	//销毁内存DC
	memdc.DeleteDC( );//::DeleteDC
}
相关推荐
charade3123 小时前
【C语言】内存分配的理解
c语言·开发语言·c++
雾削木6 小时前
mAh 与 Wh:电量单位的深度解析
开发语言·c++·单片机·嵌入式硬件·算法·电脑
Ethon_王8 小时前
走进Qt--工程文件解析与构建系统
c++·qt
工藤新一¹9 小时前
C++/SDL进阶游戏开发 —— 双人塔防游戏(代号:村庄保卫战 13)
c++·游戏·游戏引擎·毕业设计·sdl·c++游戏开发·渲染库
让我们一起加油好吗9 小时前
【C++】类和对象(上)
开发语言·c++·visualstudio·面向对象
好想有猫猫9 小时前
【Redis】服务端高并发分布式结构演进之路
数据库·c++·redis·分布式·缓存
不是杠杠9 小时前
驼峰命名法(Camel Case)与匈牙利命名法(Hungarian Notation)详解
c++
Epiphany.5569 小时前
基于c++的LCA倍增法实现
c++·算法·深度优先
落羽的落羽9 小时前
【落羽的落羽 C++】vector
c++
newki10 小时前
学习笔记,Linux虚拟机中C/C++的编译相关流程步骤
c语言·c++