目录
CPaintDC,封装了在WM_PAINT消息中绘图的绘图设备
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类(绘图对象类),**封装了各种绘图对象相关的操作
拿画笔举例子:
- 创建一根画笔
- 把画笔给绘图设备
- 使用画笔画画
- 要回画笔
- 销毁画笔
创建如下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();
}
位图:
- 添加位图资源
- 创建一个和当前DC相匹配的内存DC
- 将位图的数据给内存DC
- 成像
- 将位图数据要回来
- 销毁位图
- 销毁内存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
}