选择区域截图--基于MFC对话框

希望我们不仅把编程当成一份工作,更要将其当成一份热爱!!!

                                                                                                                                  -----潜意识中有个想成为一名厉害的程序员的梦

1.功能介绍

鼠标按下开始选择截图区域,鼠标移动选择截图区域,鼠标抬起截图完毕,保存图片。

2.主要思路

2.1 设置对话框:

  • 铺满全屏(使得全屏鼠标事件被接收)
  • 删除所有组件(菜单栏、标题栏等)
  • 设置为透明

2.2 鼠标事件处理:

  • 鼠标按下事件:标记开始选择区域,并记录当前鼠标坐标为矩形区域起始点
  • 鼠标移动事件:如果选择标识为true,则记录当前鼠标坐标为矩形区域结束点,触发重绘 调用OnPaint()来根据我们记录的起始点和结束点绘制矩形。
  • 鼠标抬起事件:根据我们记录的区域起始点和结束点构成的矩形区域进行截图保存

2.3 截图操作(这块挺复杂的,要是不想看,可以不看,直接拿代码用就完了):

  • 将我们记录的区域起始点、区域结束点围成的矩形区域,通过内存DC保存到位图中
  • 将位图存入一块内存中
  • 将内存中的数据、位图文件头信息、位图信息头信息写入文件中

3.细节代码

3.1 对话框设置

  • 先把对话框的控件全删除了:
  • 初始化中设置对话框样式:
BOOL CMfcScreenShotDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	// 使窗口铺满屏幕
	CRect screenRect;
	::SystemParametersInfo(SPI_GETWORKAREA, 0, &screenRect, 0);
	MoveWindow(&screenRect);

	// 设置窗口样式,去掉图标
	LONG style = GetWindowLong(GetSafeHwnd(), GWL_STYLE);
	style &= ~(WS_CAPTION | WS_THICKFRAME); // 去掉标题栏和边框
	SetWindowLong(GetSafeHwnd(), GWL_STYLE, style);
	SetWindowPos(&wndTopMost, 0, 0, screenRect.Width(), screenRect.Height(), SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOMOVE);

	//设置允许透明
	style |= WS_EX_LAYERED;
	SetWindowLong(m_hWnd, GWL_EXSTYLE, style);

	// 设置透明度
	// 这里设置背景色为黑色 (RGB(0, 0, 0)) 作为透明色,透明度为 200 (0-255)
	SetLayeredWindowAttributes(RGB(0, 0, 0), 100, LWA_COLORKEY | LWA_ALPHA);

	return TRUE; 
}

3.2 鼠标事件处理

  • 鼠标按下事件:
void CMfcScreenShotDlg::OnLButtonDown(UINT nFlags, CPoint point)
{
	//开始选择区域
	m_bCapturing = TRUE;
	//记录当前鼠标为选择区域的起始坐标
	m_ptStart = point;

	CDialogEx::OnLButtonDown(nFlags, point);
}
  • 鼠标移动事件:
void CMfcScreenShotDlg::OnMouseMove(UINT nFlags, CPoint point)
{
	//如果开始了区域选择
	if (m_bCapturing)
	{
		//记录当前鼠标坐标为矩形区域的结束坐标
		m_ptEnd = point;
		//触发重绘,调用OnPaint()
		Invalidate();
	}
	CDialogEx::OnMouseMove(nFlags, point);
}
  • 鼠标抬起事件:
void CMfcScreenShotDlg::OnLButtonUp(UINT nFlags, CPoint point)
{
	//如果开始了区域选择
	if (m_bCapturing)
	{
		//停止区域选择
		m_bCapturing = FALSE;
		//记录当前鼠标坐标为矩形区域的结束坐标
		m_ptEnd = point;

		// 开始截图保存,根据我们记录的矩形起始坐标和结束坐标。
		CaptureScreen();
	}
	CDialogEx::OnLButtonUp(nFlags, point);
}

3.3 绘图事件处理

void CMfcScreenShotDlg::OnPaint()
{
	    //设备上下文,用来绘图的。至于为什么叫设备上下文,咱也不知道,起名字挺迷的
		CPaintDC dc(this); 

		//如果开始区域选择
		if (m_bCapturing)
		{
			//根据我们记录的起始坐标 和 结束坐标 新建一个矩形
			CRect rect(m_ptStart, m_ptEnd);

			//刷子是用来填充的,这里不做填充
			CBrush brush;
			brush.CreateStockObject(NULL_BRUSH); // 不填充矩形
			CBrush* pOldBrush = dc.SelectObject(&brush);

			//笔是用来绘制边框的
			CPen pen(PS_SOLID, 1, RGB(255, 0, 0)); // 红色画笔
			CPen* pOldPen = dc.SelectObject(&pen);

			//绘制矩形
			dc.Rectangle(&rect);

			//还原之前的刷子和笔
			dc.SelectObject(pOldPen);
			dc.SelectObject(pOldBrush);
		}

		CDialogEx::OnPaint();
}

3.4 截图

//主打的就是繁琐
void CMfcScreenShotDlg::CaptureScreen()
{
	// 获取屏幕的设备上下文  
	HDC hScreenDC = :: GetDC(NULL);
	// 创建内存上下文,用于将所选区域绘制到新建的位图中
	HDC hMemoryDC = CreateCompatibleDC(hScreenDC);

	// 计算矩形区域的宽度和高度  
	int width = abs(m_ptEnd.x - m_ptStart.x);
	int height = abs(m_ptEnd.y - m_ptStart.y);

	// 创建一个与屏幕设备上下文兼容的位图  
	HBITMAP hBitmap = CreateCompatibleBitmap(hScreenDC, width, height);
	//将位图给到内存上下文
	HBITMAP hOldBitmap = (HBITMAP)SelectObject(hMemoryDC, hBitmap);

	// 将所选区域绘制到新建的位图中
	BitBlt(hMemoryDC, 0, 0, width, height, hScreenDC, min(m_ptStart.x, m_ptEnd.x), min(m_ptStart.y, m_ptEnd.y), SRCCOPY);
	hBitmap = (HBITMAP)SelectObject(hMemoryDC, hOldBitmap);

	// 位图的信息头
	BITMAPINFOHEADER bi;
	bi.biSize = sizeof(BITMAPINFOHEADER);
	bi.biWidth = width;
	bi.biHeight = -height;  // top-down DIB  
	bi.biPlanes = 1;
	bi.biBitCount = 24;
	bi.biCompression = BI_RGB;
	bi.biSizeImage = 0;
	bi.biXPelsPerMeter = 0;
	bi.biYPelsPerMeter = 0;
	bi.biClrUsed = 0;
	bi.biClrImportant = 0;

	//位图的大小(字节)
	DWORD dwBmpSize = ((width * bi.biBitCount + 31) / 32) * 4 * height;

	//新建内存,用来存位图
	HANDLE hDIB = GlobalAlloc(GHND, dwBmpSize);
	char* lpbitmap = (char*)GlobalLock(hDIB);

	//将位图存入内存
	GetDIBits(hScreenDC, hBitmap, 0, (UINT)height, lpbitmap, (BITMAPINFO*)&bi, DIB_RGB_COLORS);

	// 将内存中的位图数据,存入文件
	CFile file;
	if (file.Open(_T("screenshot.bmp"), CFile::modeCreate | CFile::modeWrite))
	{
		//位图的文件头
		BITMAPFILEHEADER bfh;
		bfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
		bfh.bfSize = dwBmpSize + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
		bfh.bfType = 0x4D42; // BM  
		//写入文件头
		file.Write(&bfh, sizeof(BITMAPFILEHEADER));
		//写入信息头
		file.Write(&bi, sizeof(BITMAPINFOHEADER));
		//写入位图
		file.Write(lpbitmap, dwBmpSize);
		file.Close();
	}

	// 清理资源
	GlobalUnlock(hDIB);
	GlobalFree(hDIB);
	DeleteObject(hBitmap);
	DeleteDC(hMemoryDC);
	::ReleaseDC(NULL, hScreenDC);
}

3.5 程序退出

鼠标右键抬起事件:

void CMfcScreenShotDlg::OnRButtonUp(UINT nFlags, CPoint point)
{

	EndDialog(IDCANCEL);

	// 终止程序
	PostQuitMessage(0);
	CDialogEx::OnRButtonUp(nFlags, point);
}

4.缺陷

  • 我希望截图的区域变成完全透明,但现在不是,以至于截取的图片存在模糊现象。
  • 不支持矩形框的拖动和拉拽。
  • 不支持确认截图的操作。

5.项目源码

SctoC/Mfc_ScreenShot: 基于MFC对话框的鼠标选择矩形区域进行截图的程序 (github.com)

  • 18
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于C++基于MFC设置对话框、所有控件和字体自适应放缩程序,可以采用以下步骤: 1. 在对话框类的头文件中添加如下成员变量: ```cpp CRect m_rectDlg; // 对话框矩形区域 CFont m_font; // 字体 ``` 2. 在OnInitDialog函数中初始化成员变量: ```cpp BOOL CMyDialog::OnInitDialog() { CDialogEx::OnInitDialog(); // 获取对话框矩形区域 GetClientRect(m_rectDlg); // 获取字体 LOGFONT lf; GetFont()->GetLogFont(&lf); m_font.CreateFontIndirect(&lf); // 设置所有控件自适应放缩 ModifyStyleEx(0, WS_EX_CONTROLPARENT); return TRUE; } ``` 3. 在OnSize函数中实现对所有控件的自适应放缩: ```cpp void CMyDialog::OnSize(UINT nType, int cx, int cy) { CDialogEx::OnSize(nType, cx, cy); // 获取当前对话框矩形区域 CRect rect; GetClientRect(rect); // 计算放缩比例 float fRateX = (float)rect.Width() / (float)m_rectDlg.Width(); float fRateY = (float)rect.Height() / (float)m_rectDlg.Height(); // 放缩字体 LOGFONT lf; m_font.GetLogFont(&lf); lf.lfHeight = (LONG)(lf.lfHeight * fRateY); m_font.Detach(); m_font.CreateFontIndirect(&lf); SetFont(&m_font); // 放缩控件 CWnd* pChild = GetWindow(GW_CHILD); while (pChild) { CRect rectChild; pChild->GetWindowRect(rectChild); ScreenToClient(rectChild); rectChild.left = (int)(rectChild.left * fRateX); rectChild.right = (int)(rectChild.right * fRateX); rectChild.top = (int)(rectChild.top * fRateY); rectChild.bottom = (int)(rectChild.bottom * fRateY); pChild->MoveWindow(rectChild); pChild = pChild->GetNextWindow(); } // 更新对话框矩形区域 m_rectDlg = rect; } ``` 通过以上步骤,即可实现C++基于MFC设置对话框、所有控件和字体自适应放缩程序。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值