MFC开发:图形的绘制

一、获取指定窗口的设备上下文

1.GetDC()函数的作用
GetDC() 是 Windows API 中的一个函数,它用于获取指定窗口的设备上下文(Device Context,简称 DC)。设备上下文是 GDI(图形设备接口)用于绘图的关键对象,它封装了与设备(如屏幕或打印机)相关的绘图信息。

函数原型:

HDC GetDC(HWND hWnd);

参数:

  • hWnd:指定窗口的句柄。如果 hWnd 为 NULL,则返回整个屏幕的设备上下文(相当于桌面)。否则,返回指定窗口的设备上下文。

返回值:

  • 成功:返回指定窗口的设备上下文句柄(HDC)。
  • 否则,返回指定窗口的设备上下文。

用法:
GetDC() 主要用于获取窗口的 DC,以便进行绘图操作。一般用于:

  • 在窗口客户区绘图(不包括标题栏、菜单栏等)。
  • 获取屏幕 DC 进行全屏截图等操作。

2.获取屏幕DC进行截图
在窗口客户区绘图:

#include <windows.h>

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
    switch (message) {
    case WM_PAINT: {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hWnd, &ps); // 获取DC
        TextOut(hdc, 50, 50, L"Hello, Windows!", 15); // 绘制文本
        EndPaint(hWnd, &ps); // 释放DC
        break;
    }
    case WM_LBUTTONDOWN: {
        HDC hdc = GetDC(hWnd); // 获取窗口DC
        TextOut(hdc, 100, 100, L"Mouse Click!", 12); // 绘制文本
        ReleaseDC(hWnd, hdc); // 释放DC
        break;
    }
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

说明:

  • GetDC(hWnd) 直接获取窗口的 DC,不用 BeginPaint(),适用于即时绘图(如鼠标点击)。
  • 用完 DC 后,必须调用 ReleaseDC(hWnd, hdc); 释放它,否则会导致资源泄露。

获取屏幕 DC 进行截图:

HDC hScreenDC = GetDC(NULL); // 获取整个屏幕的DC
HDC hMemDC = CreateCompatibleDC(hScreenDC);
HBITMAP hBitmap = CreateCompatibleBitmap(hScreenDC, 1920, 1080);
SelectObject(hMemDC, hBitmap);
BitBlt(hMemDC, 0, 0, 1920, 1080, hScreenDC, 0, 0, SRCCOPY);
ReleaseDC(NULL, hScreenDC);
DeleteDC(hMemDC);

说明:

  • GetDC(NULL) 获取整个屏幕的 DC。
  • BitBlt() 将屏幕内容拷贝到 hMemDC 进行截图。
  • 使用完 DC 后,需要调用 ReleaseDC() 释放它。

注意事项:

  • GetDC() 只适用于客户区绘图,如果要绘制整个窗口(包括非客户区,如标题栏),请用 GetWindowDC()。
  • 必须调用 ReleaseDC() 释放 DC,GetDC() 分配的 DC 需要 ReleaseDC() 释放,否则会导致资源泄露。
  • 与 BeginPaint() 的区别:GetDC() 适用于即时绘图,适合响应鼠标事件等。BeginPaint() 仅在 WM_PAINT 处理时使用,并会自动更新无效区域,适用于窗口重绘。

二、画笔的介绍和使用

CPen 是 MFC(Microsoft Foundation Class) 提供的一个 GDI(图形设备接口)对象,用于在 Windows 应用程序中创建和管理画笔(Pen)。画笔用于绘制直线、边框、曲线 等图形元素。

1. CPen 类的基本介绍
CPen 继承自 CGdiObject,用于定义画笔的颜色、线条样式和宽度,并可以与 CDC(设备上下文)一起使用进行绘图。

2. CPen 的构造函数

CPen(int nPenStyle, int nWidth, COLORREF crColor);

参数说明:

  • nPenStyle:线条样式,如 实线、虚线、点线 等(见下表)。
  • nWidth:线条宽度(像素)。
  • crColor:颜色,使用 RGB(r, g, b) 指定。

3. 线条样式 (nPenStyle)
在这里插入图片描述
4. CPen 的使用
(1)创建和使用画笔

void CMyView::OnDraw(CDC* pDC) {
    // 创建一个红色、2 像素宽的实线画笔
    CPen pen(PS_SOLID, 2, RGB(255, 0, 0));
    
    // 选择画笔,并保存旧画笔
    CPen* pOldPen = pDC->SelectObject(&pen);

    // 画一条直线
    pDC->MoveTo(50, 50);
    pDC->LineTo(200, 200);

    // 恢复旧画笔
    pDC->SelectObject(pOldPen);
}

说明:

  • SelectObject(&pen) 选择新画笔,并返回旧画笔指针(必须恢复)。
  • MoveTo(x, y) 移动到起点。
  • LineTo(x, y) 画一条直线。

(2)使用 CPen 变量

void CMyView::OnDraw(CDC* pDC) {
    CPen bluePen(PS_DOT, 3, RGB(0, 0, 255));  // 蓝色点线,3px 宽
    CPen* pOldPen = pDC->SelectObject(&bluePen);

    pDC->Ellipse(50, 50, 200, 200);  // 画一个椭圆

    pDC->SelectObject(pOldPen);  // 恢复原画笔
}

(3)使用 CreatePen()

void CMyView::OnDraw(CDC* pDC) {
    CPen pen;
    pen.CreatePen(PS_DASHDOT, 5, RGB(0, 255, 0)); // 绿色虚点线,5px 宽

    CPen* pOldPen = pDC->SelectObject(&pen);

    pDC->Rectangle(100, 100, 300, 300); // 画一个矩形

    pDC->SelectObject(pOldPen);
}

(4)创建 LOGBRUSH 自定义画笔

void CMyView::OnDraw(CDC* pDC) {
    LOGBRUSH lb;
    lb.lbStyle = BS_SOLID;  // 纯色填充
    lb.lbColor = RGB(255, 165, 0); // 橙色
    lb.lbHatch = 0;  

    CPen pen;
    pen.CreatePen(PS_SOLID | PS_ENDCAP_ROUND, 10, &lb); // 圆头 10px 画笔

    CPen* pOldPen = pDC->SelectObject(&pen);

    pDC->MoveTo(50, 50);
    pDC->LineTo(300, 50);

    pDC->SelectObject(pOldPen);
}

5. CPen 使用注意事项

  • 必须恢复旧画笔:SelectObject() 之前存储旧画笔,绘制完毕后恢复它,避免影响后续绘图。
  • 不要在 OnPaint() 中创建临时 CPen 对象:CPen 对象应在 OnDraw() 或 OnPaint() 内部创建并销毁,避免资源泄漏。
  • CPen 对象生存期要合理:如果是临时画笔,直接在 OnDraw() 里使用,不要 new 分配。若 CPen 需要在多个函数中使用,应定义为 成员变量,避免频繁创建销毁。

三、绘制直线

void CChildView::OnLButtonDown(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	
	// 保存起点位置到成员变量中
	m_pOrigin = point;
	CWnd::OnLButtonDown(nFlags, point);
}

void CChildView::OnLButtonUp(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值

	// 画线
	// 获得设备上下文
	CDC* pDC = GetDC();

	// 画笔移动到起始点
	pDC->MoveTo(m_pOrigin);
	// 从起点到终点画一条直线
	pDC->LineTo(point);

	// 释放设备上下文
	ReleaseDC(pDC);

	CWnd::OnLButtonUp(nFlags, point);
}

结果展示:
在这里插入图片描述

四、画刷的介绍和使用

CBrush 是 MFC(Microsoft Foundation Classes)提供的 GDI(图形设备接口) 对象之一,用于创建和管理 画刷(Brush)。画刷主要用于填充图形的 内部区域,例如填充矩形、椭圆、圆形、区域等。

1. CBrush 类的基本介绍
CBrush 继承自 CGdiObject,用于定义画刷的 颜色、填充模式和样式,并可与 CDC(设备上下文)一起使用进行绘图。

2. CBrush 的构造函数

CBrush(COLORREF crColor);
CBrush(int nIndex, COLORREF crColor);
CBrush(int nStyle, COLORREF crColor);
CBrush(int nStyle, CBitmap* pBitmap);

参数说明:

  • crColor:画刷的颜色(使用 RGB(r, g, b) 指定)。
  • nIndex:系统颜色索引(如 COLOR_WINDOW)。
  • nStyle:画刷样式(如 HS_DIAGCROSS、HS_HORIZONTAL)。
  • pBitmap:用于创建 位图画刷 的 CBitmap 指针。

3. CBrush 画刷样式
在这里插入图片描述
4. CBrush 的使用
(1)创建纯色画刷

void CMyView::OnDraw(CDC* pDC) {
    CBrush brush(RGB(255, 0, 0)); // 创建红色画刷
    CBrush* pOldBrush = pDC->SelectObject(&brush);

    pDC->Rectangle(50, 50, 200, 200); // 画填充矩形

    pDC->SelectObject(pOldBrush); // 恢复旧画刷
}

说明:

  • SelectObject(&brush) 选择画刷,并返回原来的画刷(后续需要恢复)。
  • Rectangle(x1, y1, x2, y2) 使用画刷填充矩形。

(2)创建带有样式的画刷

void CMyView::OnDraw(CDC* pDC) {
    CBrush brush(HS_CROSS, RGB(0, 255, 0)); // 绿色十字交叉填充
    CBrush* pOldBrush = pDC->SelectObject(&brush);

    pDC->Ellipse(50, 50, 200, 200); // 画填充椭圆

    pDC->SelectObject(pOldBrush);
}

常见 HS_ 样式:
在这里插入图片描述
(3)创建 CreateSolidBrush()

void CMyView::OnDraw(CDC* pDC) {
    CBrush brush;
    brush.CreateSolidBrush(RGB(0, 0, 255)); // 创建蓝色画刷

    CBrush* pOldBrush = pDC->SelectObject(&brush);
    pDC->Rectangle(100, 100, 300, 300); // 填充矩形

    pDC->SelectObject(pOldBrush);
}

说明:

  • CreateSolidBrush(RGB(0,0,255)) 以指定颜色创建画刷。
  • 适用于动态创建 画刷的情况。

(4)创建位图填充画刷

void CMyView::OnDraw(CDC* pDC) {
    CBitmap bitmap;
    bitmap.LoadBitmap(IDB_BITMAP1); // 加载资源中的位图

    CBrush brush;
    brush.CreatePatternBrush(&bitmap); // 创建位图画刷

    CBrush* pOldBrush = pDC->SelectObject(&brush);
    pDC->Rectangle(50, 50, 250, 250); // 用位图填充矩形

    pDC->SelectObject(pOldBrush);
}

说明:

  • CreatePatternBrush(&bitmap) 使用位图创建画刷,适用于 纹理填充。
  • LoadBitmap(IDB_BITMAP1) 加载 资源位图,IDB_BITMAP1 需要在 资源管理器 中定义。

(5)结合 CPen 和 CBrush 使用

void CMyView::OnDraw(CDC* pDC) {
    CPen pen(PS_SOLID, 2, RGB(0, 0, 255));  // 蓝色边框
    CBrush brush(RGB(255, 255, 0));         // 黄色填充

    CPen* pOldPen = pDC->SelectObject(&pen);
    CBrush* pOldBrush = pDC->SelectObject(&brush);

    pDC->Rectangle(50, 50, 200, 200); // 画填充的矩形

    pDC->SelectObject(pOldPen);
    pDC->SelectObject(pOldBrush);
}

说明:

  • CPen 负责边框颜色,CBrush 负责填充颜色。
  • Rectangle(x1, y1, x2, y2) 画出填充的矩形。

5. CBrush 使用注意事项

  • 必须恢复旧画刷:SelectObject() 返回旧画刷,需要在绘制完成后恢复它,避免影响后续绘图。
  • 不要在 OnPaint() 中创建临时 CBrush:CBrush 应该在 OnDraw() 或 OnPaint() 内部创建并销毁,以避免资源泄露。
  • 使用 CreateSolidBrush() 动态创建画刷:如果 CBrush 需要在多个函数中使用,推荐 使用成员变量 避免重复创建。

五、绘制扇形

void CChildView::OnLButtonDown(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	
	// 保存起点位置到成员变量中
	m_pOrigin = point;
	m_bDraw = true;
	
	CWnd::OnLButtonDown(nFlags, point);
}

void CChildView::OnLButtonUp(UINT nFlags, CPoint point)
{
	m_bDraw = false;
	CWnd::OnLButtonUp(nFlags, point);
}


void CChildView::OnMouseMove(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	
	if (m_bDraw) {
		CClientDC dc(this);
		
		dc.MoveTo(m_pOrigin);
		dc.LineTo(point);

		m_pOrigin = point;
	}

	CWnd::OnMouseMove(nFlags, point);
}

在这里插入图片描述

void CChildView::OnLButtonDown(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	
	// 保存起点位置到成员变量中
	m_pOrigin = point;
	m_bDraw = true;
	
	CWnd::OnLButtonDown(nFlags, point);
}

void CChildView::OnLButtonUp(UINT nFlags, CPoint point)
{
	m_bDraw = false;
	CWnd::OnLButtonUp(nFlags, point);
}


void CChildView::OnMouseMove(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	
	if (m_bDraw) {
		CClientDC dc(this);
		CPen pen(PS_SOLID, 1, RGB(0, 255, 255));
		CPen* pOldPen = dc.SelectObject(&pen);
		
		dc.MoveTo(m_pOrigin);
		dc.LineTo(point);

		dc.SelectObject(pOldPen);
	}

	CWnd::OnMouseMove(nFlags, point);
}

在这里插入图片描述

六、绘制圆形

void CChildView::OnLButtonDown(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	
	// 保存起点位置到成员变量中
	m_pOrigin = point;
	m_bDraw = true;
	
	CWnd::OnLButtonDown(nFlags, point);
}

void CChildView::OnLButtonUp(UINT nFlags, CPoint point)
{
	m_bDraw = false;

	CClientDC dc(this);
	
	CPen pen(PS_DASH, 1, RGB(0, 0, 255));
	CPen* pOldpen = dc.SelectObject(&pen);
	
	// 画椭圆
	//dc.Ellipse(CRect(m_pOrigin, point));
	
	// 计算两点之间x坐标的差值
	long len = point.x - m_pOrigin.x;
	dc.Ellipse(m_pOrigin.x, m_pOrigin.y, m_pOrigin.x + len, m_pOrigin.y + len);
	dc.SelectObject(pOldpen);

	CWnd::OnLButtonUp(nFlags, point);
}

在这里插入图片描述

七、绘制文本

void CChildView::OnLButtonUp(UINT nFlags, CPoint point)
{
	CString str(_T("你好我好大家好!"));

	CClientDC dc(this);
	
	CFont font;
	// 设置字体
	font.CreatePointFont(200, _T("宋体"));
	CFont* pOldFont = dc.SelectObject(&font);


	// 设置文字颜色
	dc.SetTextColor(RGB(125, 0, 127));
	// 输出字符串
	dc.TextOutW(point.x, point.y, str);
	dc.SelectObject(pOldFont);

	CWnd::OnLButtonUp(nFlags, point);
}

在这里插入图片描述

八、制作动画文字

在 MFC(Microsoft Foundation Classes) 中,OnCreate 处理 WM_CREATE 消息,用于在窗口创建时执行初始化操作。

1.OnCreate 的作用

  • 在窗口创建时执行初始化逻辑。
  • 适用于窗口控件的创建、资源的分配、定时器的启动等。

2. 关联 WM_CREATE 消息
MFC 使用 BEGIN_MESSAGE_MAP 将 OnCreate 绑定到 WM_CREATE:

BEGIN_MESSAGE_MAP(CMyWnd, CWnd)
    ON_WM_CREATE() // 绑定 OnCreate
END_MESSAGE_MAP()

3.添加WM_CREATE 消息
在这里插入图片描述

4.添加定时器
在这里插入图片描述
示例代码:

#define TIMER_TEXT 9526

int CChildView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CWnd::OnCreate(lpCreateStruct) == -1)
		return -1;

	// TODO:  在此添加您专用的创建代码
	// 添加定时器
	SetTimer(TIMER_TEXT, 200, NULL);

	return 0;
}


void CChildView::OnTimer(UINT_PTR nIDEvent)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值

	switch (nIDEvent)
	{
	case TIMER_TEXT: {
		m_nWidth += 10;
		CString str(_T("你好我好大家好!"));

		CClientDC dc(this);

		CFont font;
		// 设置字体
		font.CreatePointFont(200, _T("宋体"));
		CFont* pOldFont = dc.SelectObject(&font);

		// 设置文字颜色
		dc.SetTextColor(RGB(125, 0, 127));

		// 获取文字在屏幕上的空间大小,长度和宽度
		CSize cz = dc.GetTextExtent(str);

		CRect rect;
		rect.left = 200;
		rect.top = 100;
		rect.bottom = rect.top + cz.cy;
		rect.right = rect.left + m_nWidth;

		// 画文字,再指定矩形区域内
		/*
		DT_LEFT:从左边开始画
		DT_RIGHT:从右边开始画
		DT_CENTER:从中间位置开始画
		*/
		dc.DrawText(str, rect, DT_LEFT);

		if (m_nWidth > cz.cx) {
			m_nWidth = 0;
			//使屏幕无效,擦除屏幕上内容
			Invalidate();
		}

		dc.SelectObject(pOldFont);
	}
		break;
	default:
		break;
	}

	CWnd::OnTimer(nIDEvent);
}

效果展示:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值