概述
screencapture 是一个辅助类,用于在 windows forms 应用程序中实现全屏区域截图功能。它提供了一个半透明覆盖层,用户可以按下鼠标左键并拖动选择一个矩形区域,松开鼠标后即可截取该区域的图像。
该类的设计初衷是配合 picturebox 控件,让用户通过“截图”按钮快速截取屏幕任意区域,并自动加载到图片展示控件中。
主要功能
- 全屏半透明遮罩,高亮显示鼠标拖拽的选区
- 支持 esc 键取消截图
- 返回截图的
bitmap对象,可进一步转换为opencvsharp.mat或其他图像格式 - 实现了
idisposable接口,便于资源管理
使用方法
1. 在项目中添加文件
将 screencapture.cs 添加到winforms 项目中。
2. 基本调用示例
using (var screencapture = new screencapture())
{
bitmap capturedbmp = screencapture.capturescreen();
if (capturedbmp != null)
{
// 将 bitmap 转换为 opencvsharp.mat(需引用 opencvsharp.extensions)
mat mat = bitmapconverter.tomat(capturedbmp);
// 显示到 picturebox
picturebox1.image?.dispose();
picturebox1.image = mat.tobitmap();
}
}3. 配合按钮点击事件使用(标准用法)
private void btnscreenshot_click(object sender, eventargs e)
{
// 隐藏当前窗体,避免遮挡截图界面
this.hide();
// 等待窗体完全隐藏
system.threading.thread.sleep(200);
using (var cap = new screencapture())
{
var bmp = cap.capturescreen();
if (bmp != null)
{
// 处理截图结果,例如显示在 picturebox 中
picturebox1.image?.dispose();
picturebox1.image = bmp;
}
}
// 重新显示主窗体
this.show();
}4. 其他示例
private void btnscreenshotocr_click(object sender, eventargs e)
{
takescreenshot(img =>
{
currentocrimage?.dispose();
currentocrimage = img.clone();
showimage(pictureboxocr, img);
});
}
private void takescreenshot(action<mat> oncaptured)
{
this.hide();
system.threading.thread.sleep(200);
using (var cap = new screencapture())
{
var bmp = cap.capturescreen();
if (bmp != null)
{
mat mat = opencvsharp.extensions.bitmapconverter.tomat(bmp);
oncaptured?.invoke(mat);
}
}
this.show();
}注意事项
截图期间主窗体隐藏
为了获得纯净的截图背景,通常需要将主窗体隐藏(this.hide()),截图完成后再显示(this.show())。注意等待一小段时间(如 200ms)确保窗体完全隐藏。
屏幕 dpi 缩放
在高 dpi 环境下,graphics.copyfromscreen 会按物理屏幕坐标截取,通常没有问题。如果需要考虑缩放比例,可以进一步调整。
取消截图
用户按下 esc 键后,dialogresult 会返回 cancel,capturescreen() 返回 null。您的代码应当处理 null 情况。
线程安全screencapture 内部使用 showdialog() 显示模态覆盖层,必须在 ui 线程调用。不要在后台线程中直接调用。
资源释放
类实现了 idisposable,务必使用 using 语句或手动调用 dispose() 释放内部资源(如覆盖层窗体)。
内部结构说明
selectionoverlay是一个继承自form的内部类,负责显示半透明全屏遮罩,处理鼠标拖拽和键盘事件。selectedregion属性记录了用户选中的矩形区域(屏幕坐标)。- 截图操作通过
graphics.copyfromscreen将选中区域复制到bitmap中。
依赖项
- 需要引用
system.drawing和system.windows.forms。 - 如需转换为
mat,还需要opencvsharp.extensions(可选)。
using system;
using system.drawing;
using system.drawing.imaging;
using system.windows.forms;
namespace windowsformsapp1
{
/// <summary>
/// 屏幕截图辅助类,提供全屏区域截图功能。
/// 使用方法:
/// <code>
/// using (var cap = new screencapture())
/// {
/// bitmap bmp = cap.capturescreen();
/// if (bmp != null)
/// {
/// // 处理截图
/// }
/// }
/// </code>
/// </summary>
public class screencapture : idisposable
{
/// <summary>
/// 启动全屏选区截图,返回用户选中的区域图像。
/// </summary>
/// <returns>截取到的 bitmap 图像;如果用户取消操作或选区无效,返回 null。</returns>
public bitmap capturescreen()
{
// 创建并显示选区覆盖层窗体(模态对话框)
using (var overlay = new selectionoverlay())
{
// 显示对话框,等待用户操作
var result = overlay.showdialog();
// 用户确认且区有效
if (result == dialogresult.ok && overlay.selectedregion != rectangle.empty)
{
rectangle bounds = overlay.selectedregion;
// 创建与选区相同尺寸的 bitmap
bitmap bmp = new bitmap(bounds.width, bounds.height, pixelformat.format32bppargb);
using (graphics g = graphics.fromimage(bmp))
{
// 从屏幕复制选区内容到 bitmap
g.copyfromscreen(bounds.x, bounds.y, 0, 0, bounds.size);
}
return bmp;
}
}
return null;
}
/// <summary>
/// 全屏选区覆盖层窗体(内部类),提供半透明背景和鼠标拖拽选择功能。
/// </summary>
private class selectionoverlay : form
{
/// <summary>用户最终选中的屏幕区域(屏幕坐标)。</summary>
public rectangle selectedregion { get; private set; } = rectangle.empty;
private point startpoint; // 鼠标按下时的起始点
private bool selecting = false; // 是否正在拖拽选择中
private rectangle currentrect; // 当前拖拽的矩形
private pen selectionpen; // 绘制选择框的画笔
/// <summary>
/// 初始化覆盖层窗体。
/// </summary>
public selectionoverlay()
{
// 无边框、最大化填满屏幕
this.formborderstyle = formborderstyle.none;
this.windowstate = formwindowstate.maximized;
// 黑色半透明背景,实现“遮罩”效果
this.backcolor = color.black;
this.opacity = 0.6; // 透明度 0.6,突出选框区域
this.doublebuffered = true; // 减少闪烁
this.topmost = true; // 置顶,覆盖所有窗口
this.cursor = cursors.cross; // 十字光标,适合选区操作
this.keypreview = true; // 让窗体优先接收键盘事件(如 esc)
// 初始化画笔:半透明绿色,2像素宽
selectionpen = new pen(color.fromargb(100, 0, 255, 0), 2);
// 绑定事件
this.mousedown += onmousedown;
this.mousemove += onmousemove;
this.mouseup += onmouseup;
this.paint += onpaint;
this.keydown += onkeydown;
}
/// <summary>
/// 鼠标按下:开始选区。
/// </summary>
private void onmousedown(object sender, mouseeventargs e)
{
if (e.button == mousebuttons.left)
{
startpoint = e.location; // 记录起始点
selecting = true; // 进入选择模式
currentrect = new rectangle(startpoint, new size(0, 0)); // 初始矩形为空
invalidate(); // 触发重绘
}
}
/// <summary>
/// 鼠标移动:更新当前选区矩形并重绘。
/// </summary>
private void onmousemove(object sender, mouseeventargs e)
{
if (selecting)
{
// 计算矩形的正确边界(支持向左/向上拖拽)
int x = math.min(startpoint.x, e.x);
int y = math.min(startpoint.y, e.y);
int w = math.abs(startpoint.x - e.x);
int h = math.abs(startpoint.y - e.y);
currentrect = new rectangle(x, y, w, h);
invalidate(); // 触发 onpaint 重绘
}
}
/// <summary>
/// 鼠标释放:完成选区。
/// </summary>
private void onmouseup(object sender, mouseeventargs e)
{
if (e.button == mousebuttons.left && selecting && currentrect.width > 5 && currentrect.height > 5)
{
// 选区宽度和高度至少为5像素,避免误触
selectedregion = currentrect; // 保存选区
this.dialogresult = dialogresult.ok; // 设置对话框结果为 ok
this.close(); // 关闭覆盖层
}
selecting = false; // 退出选择模式
}
/// <summary>
/// 绘制覆盖层内容:在选区边缘绘制矩形框。
/// </summary>
private void onpaint(object sender, painteventargs e)
{
if (selecting && currentrect.width > 0 && currentrect.height > 0)
{
// 绘制矩形框(仅边框,不填充)
e.graphics.drawrectangle(selectionpen, currentrect);
}
}
/// <summary>
/// 键盘按下:按 esc 键取消截图。
/// </summary>
private void onkeydown(object sender, keyeventargs e)
{
if (e.keycode == keys.escape)
{
this.dialogresult = dialogresult.cancel; // 取消操作
this.close();
}
}
/// <summary>
/// 释放资源。
/// </summary>
protected override void dispose(bool disposing)
{
if (disposing)
{
selectionpen?.dispose(); // 释放画笔
}
base.dispose(disposing);
}
}
/// <summary>
/// 实现 idisposable 接口(当前类无额外需要释放的资源,但保留方法以备将来扩展)。
/// </summary>
public void dispose()
{
// 无托管资源需要释放,但为了接口完整性保留空方法
}
}
}到此这篇关于使用c#自制一个截屏工具的文章就介绍到这了,更多相关c#截屏工具内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论