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

相关文章

  • 深入理解.NET中的异步

    深入理解.NET中的异步

    异步编程是程序设计的重点,在实际的项目,在大量的数据入库以及查询数据并进行计算的时候,程序的UI界面往往卡死在那里,这时候就需要对计算时间限制的过程进行异步处理,同时正确的使用异步编程去处理计算限制的操作和耗时IO操作还能提升的应用程序的吞吐量及性能
    2021-06-06
  • C#实现鼠标左右键切换效果

    C#实现鼠标左右键切换效果

    这篇文章主要为大家详细介绍了如何利用C#实现鼠标左右键切换功能,文中的示例代码讲解详细,对我们学习C#有一定的帮助,感兴趣的小伙伴可以跟随小编一起了解一下
    2022-12-12
  • C#实现生成指定图片的缩略图

    C#实现生成指定图片的缩略图

    这篇文章主要为大家详细介绍了如何使用C#实现生成指定图片的缩略图,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2024-04-04
  • C# 添加Word文本和图片超链接的方法

    C# 添加Word文本和图片超链接的方法

    本文给大家介绍如何用C#编程语言对Word文档中的文本和图片进行超链接设置。感兴趣的朋友一起看看吧
    2017-10-10
  • C# WinForm打开PDF文件并在窗体中显示

    C# WinForm打开PDF文件并在窗体中显示

    本文主要介绍通过引用Adobe reader提供的COM组件,以实现在WinForm程序中显示PDF文件的功能。
    2016-05-05
  • C#执行表达式树(Expression Tree)的具体使用

    C#执行表达式树(Expression Tree)的具体使用

    本文将深入探讨表达式树的基本概念、创建方法、修改和删除节点、查询和遍历技巧以及在C#中的应用示例,具有一定的参考价值,感兴趣的可以了解一下
    2024-03-03
  • C# 读写自定义的Config文件的实现方法

    C# 读写自定义的Config文件的实现方法

    本文主要介绍了C# 读写自定义的Config文件的实现方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-07-07
  • C#微信公众号开发之用户管理

    C#微信公众号开发之用户管理

    这篇文章介绍了C#微信公众号开发之用户管理,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-06-06
  • C#中子类调用父类的实现方法

    C#中子类调用父类的实现方法

    这篇文章主要介绍了C#中子类调用父类的实现方法,通过实例逐步分析了类中初始化构造函数的执行顺序问题,有助于加深对C#面向对象程序设计的理解,需要的朋友可以参考下
    2014-09-09
  • C#多线程TPL模式高级用法探秘

    C#多线程TPL模式高级用法探秘

    本文详细讲解了C#多线程TPL模式的高级用法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-03-03

最新评论