概述
ScreenCapture 是一个辅助类,用于在 Windows Forms 应用程序中实现全屏区域截图功能。它提供了一个半透明覆盖层,用户可以按下鼠标左键并拖动选择一个矩形区域,松开鼠标后即可截取该区域的图像。
该类的设计初衷是配合 PictureBox 控件,让用户通过"截图"按钮快速截取屏幕任意区域,并自动加载到图片展示控件中。
主要功能
-
全屏半透明遮罩,高亮显示鼠标拖拽的选区
-
支持 ESC 键取消截图
-
返回截图的
Bitmap对象,可进一步转换为OpenCvSharp.Mat或其他图像格式 -
实现了
IDisposable接口,便于资源管理
使用方法
1. 在项目中添加文件
将 ScreenCapture.cs 添加到WinForms 项目中。
2. 基本调用示例
csharp
// 实例化并调用截图
cs
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. 配合按钮点击事件使用(标准用法)
cs
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. 其他示例
cs
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(可选)。
///ScreenCapture.cs
cs
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()
{
// 无托管资源需要释放,但为了接口完整性保留空方法
}
}
}