使用C#自制一个截屏工具

 更新时间:2026年04月27日 09:20:40   作者:hixiong123  
ScreenCapture 是一个辅助类,用于在 Windows Forms 应用程序中实现全屏区域截图功能,本文给大家介绍了如何使用C#自制一个截屏工具,需要的朋友可以参考下

概述

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 会返回 CancelCaptureScreen() 返回 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#截屏工具内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 详解C#异步多线程使用中的常见问题

    详解C#异步多线程使用中的常见问题

    本文主要介绍了C#异步多线程使用中的常见问题,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-01-01
  • Unity实现图片轮播组件

    Unity实现图片轮播组件

    这篇文章主要为大家详细介绍了Unity实现图片轮播组件的相关方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-03-03
  • 如何在C#中集成Lua脚本

    如何在C#中集成Lua脚本

    这篇文章主要介绍了如何在C#中集成Lua脚本,帮助大家更好的理解和学习使用c#,感兴趣的朋友可以了解下
    2021-02-02
  • c# 圆形识别方案和直线识别方案的参考示例

    c# 圆形识别方案和直线识别方案的参考示例

    这篇文章主要介绍了c# 圆形识别方案和直线识别方案的实现示例,帮助大家更好的理解和学习使用c#,感兴趣的朋友可以了解下
    2021-03-03
  • wpf实现超低延迟的RTMP或RTSP播放

    wpf实现超低延迟的RTMP或RTSP播放

    这篇文章主要为大家详细介绍了wpf如何实现超低延迟的RTMP或RTSP播放,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2024-04-04
  • 深入理解C#之接口

    深入理解C#之接口

    这篇文章主要介绍了C#接口(Interface)用法,较为详细的分析了C#中接口的功能、实现及使用技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2021-07-07
  • C#实现窗体淡入淡出效果的方法总结

    C#实现窗体淡入淡出效果的方法总结

    C#实现窗体淡入淡出效果的方法总结,需要的朋友可以参考一下
    2013-05-05
  • 详解C#中yield关键字的用法

    详解C#中yield关键字的用法

    yield 关键字的用途是把指令推迟到程序实际需要的时候再执行,这个特性允许我们更细致地控制集合每个元素产生的时机,那么下面就一起来看下怎么用 yield 关键字吧
    2023-07-07
  • Unity3d 使用Gizmos画一个圆圈

    Unity3d 使用Gizmos画一个圆圈

    这篇文章主要介绍了Unity3d 使用Gizmos画一个圆圈的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-04-04
  • Visual Studio关于C#项目Dll的引用多种方式(图文详解)

    Visual Studio关于C#项目Dll的引用多种方式(图文详解)

    本文通过图文并茂的形式给大家展示Visual Studio关于C#项目Dll的引用几种方式 ,感兴趣的朋友跟随小编一起看看吧
    2024-08-08

最新评论