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
}
相关推荐
捕鲸叉44 分钟前
怎样在软件设计中选择使用GOF设计模式
c++·设计模式
捕鲸叉1 小时前
C++设计模式和编程框架两种设计元素的比较与相互关系
开发语言·c++·设计模式
未知陨落1 小时前
数据结构——二叉搜索树
开发语言·数据结构·c++·二叉搜索树
一丝晨光2 小时前
gcc 1.c和g++ 1.c编译阶段有什么区别?如何知道g++编译默认会定义_GNU_SOURCE?
c语言·开发语言·c++·gnu·clang·gcc·g++
汉克老师3 小时前
GESP4级考试语法知识(贪心算法(四))
开发语言·c++·算法·贪心算法·图论·1024程序员节
姆路4 小时前
QT中使用图表之QChart绘制动态折线图
c++·qt
秋说4 小时前
【数据结构 | C++】整型关键字的平方探测法散列
数据结构·c++·算法
槿花Hibiscus7 小时前
C++基础:Pimpl设计模式的实现
c++·设计模式
黑不拉几的小白兔8 小时前
PTA部分题目C++重练
开发语言·c++·算法
写bug的小屁孩8 小时前
websocket身份验证
开发语言·网络·c++·qt·websocket·网络协议·qt6.3