当前位置: 首页
编程语言
使用C#自制一个截屏工具

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

热心网友 时间:2026-04-27
转载

概述

在Windows Forms应用开发中,实现一个灵活、好用的屏幕截图功能,是很多开发者都会遇到的需求。今天要介绍的ScreenCapture辅助类,正是为此而生。它封装了一套完整的全屏区域截图逻辑:启动后,屏幕会覆盖一层半透明的黑色遮罩,用户只需按住鼠标左键并拖动,就能框选出任意矩形区域,松开鼠标即可完成截取。

免费影视、动漫、音乐、游戏、小说资源长期稳定更新! 👉 点此立即查看 👈

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

这个类的设计非常贴合实际场景,通常与一个PictureBox控件配合使用。用户点击“截图”按钮后,快速选取屏幕区域,截取的图像能立刻加载到图片框中,流程一气呵成。

主要功能

简单来说,这个类提供了以下核心能力:

  • 直观的交互体验:全屏半透明遮罩,鼠标拖拽时,选中的区域会被高亮显示,操作感清晰。
  • 便捷的取消操作:任何时候,按下ESC键就能立刻退出截图模式,不会产生任何结果。
  • 灵活的返回值:直接返回标准的Bitmap对象,你可以轻松地将其转换为OpenCvSharp.Mat或其他任何需要的图像格式,方便后续处理。
  • 规范的资源管理:类实现了IDisposable接口,建议配合using语句使用,确保内部资源(如覆盖窗体)能被及时释放。

使用方法

1. 在项目中添加文件

第一步很简单,直接将ScreenCapture.cs源代码文件添加到你的WinForms项目里即可。

2. 基本调用示例

最基础的用法,几行代码就能搞定。下面这段代码展示了如何截图并转换为OpenCV的Mat格式:

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();
    // 等待窗体完全隐藏,200毫秒通常足够
    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. 其他示例

如果你的处理逻辑更复杂,或者需要复用截图代码,可以将其封装成一个带回调的方法。下面的例子展示了如何将截图用于OCR场景:

private void btnScreenshotOcr_Click(object sender, EventArgs e)
{
    TakeScreenshot(img =>
    {
        currentOcrImage?.Dispose();
        currentOcrImage = img.Clone();
        ShowImage(pictureBoxOcr, img);
    });
}

private void TakeScreenshot(Action 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())。这里有个小技巧:隐藏后最好等待一小段时间(比如200毫秒),确保窗体已经完全从屏幕上移除,再开始截图,这样更稳妥。

屏幕 DPI 缩放

在高DPI环境下,Graphics.CopyFromScreen方法会按物理屏幕坐标进行截取,所以通常情况下没有问题。如果你的应用对缩放比例有特殊要求,可能需要在此基础上做进一步的坐标转换和调整。

取消截图

用户按下ESC键后,内部窗体的DialogResult会返回Cancel,导致CaptureScreen()方法返回null。因此,你的代码里一定要记得检查返回值是否为null,并做相应的处理。

线程安全

需要特别注意,ScreenCapture内部使用了ShowDialog()来显示模态的覆盖层窗体,这意味着它必须在UI线程中调用。千万不要在后台线程里直接使用它,否则会引发跨线程操作控件的异常。

资源释放

这个类实现了IDisposable接口,内部会管理一些窗体资源。最佳实践是使用using语句来包裹它,这样即使发生异常,资源也能被正确释放。当然,手动调用Dispose()也是可以的。

内部结构说明

了解其内部构造,有助于你更灵活地使用或修改它。这个类主要包含两部分:

  • SelectionOverlay:这是一个继承自Form的内部类,是整个功能的核心。它负责显示全屏半透明遮罩,并处理所有鼠标拖拽和键盘事件(比如ESC键)。
  • SelectedRegion属性:它记录了用户最终选中的矩形区域,坐标是基于整个屏幕的。

截图的“魔法”发生在CaptureScreen方法里:当用户确认选区后,它会利用Graphics.CopyFromScreen方法,将屏幕上对应矩形区域的像素数据,原封不动地复制到一个新的Bitmap对象中。

依赖项

要使用这个类,你的项目需要引用以下基础库:

  • System.Drawing
  • System.Windows.Forms

如果你需要将截图结果转换为OpenCV的Mat格式进行图像处理,那么还需要额外引用OpenCvSharp.Extensions(这是一个可选依赖)。

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
    /// 
    /// 屏幕截图辅助类,提供全屏区域截图功能。
    /// 使用方法:
    /// 
    /// using (var cap = new ScreenCapture())
    /// {
    ///     Bitmap bmp = cap.CaptureScreen();
    ///     if (bmp != null)
    ///     {
    ///         // 处理截图
    ///     }
    /// }
    /// 
    /// 
    public class ScreenCapture : IDisposable
    {
        /// 
        /// 启动全屏选区截图,返回用户选中的区域图像。
        /// 
        /// 截取到的 Bitmap 图像;如果用户取消操作或选区无效,返回 null。
        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;
        }
        /// 
        /// 全屏选区覆盖层窗体(内部类),提供半透明背景和鼠标拖拽选择功能。
        /// 
        private class SelectionOverlay : Form
        {
            /// 用户最终选中的屏幕区域(屏幕坐标)。
            public Rectangle SelectedRegion { get; private set; } = Rectangle.Empty;
            private Point startPoint;          // 鼠标按下时的起始点
            private bool selecting = false;     // 是否正在拖拽选择中
            private Rectangle currentRect;      // 当前拖拽的矩形
            private Pen selectionPen;           // 绘制选择框的画笔
            /// 
            /// 初始化覆盖层窗体。
            /// 
            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;
            }
            /// 
            /// 鼠标按下:开始选区。
            /// 
            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();                      // 触发重绘
                }
            }
            /// 
            /// 鼠标移动:更新当前选区矩形并重绘。
            /// 
            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 重绘
                }
            }
            /// 
            /// 鼠标释放:完成选区。
            /// 
            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;  // 退出选择模式
            }
            /// 
            /// 绘制覆盖层内容:在选区边缘绘制矩形框。
            /// 
            private void OnPaint(object sender, PaintEventArgs e)
            {
                if (selecting && currentRect.Width > 0 && currentRect.Height > 0)
                {
                    // 绘制矩形框(仅边框,不填充)
                    e.Graphics.DrawRectangle(selectionPen, currentRect);
                }
            }
            /// 
            /// 键盘按下:按 ESC 键取消截图。
            /// 
            private void OnKeyDown(object sender, KeyEventArgs e)
            {
                if (e.KeyCode == Keys.Escape)
                {
                    this.DialogResult = DialogResult.Cancel;  // 取消操作
                    this.Close();
                }
            }
            /// 
            /// 释放资源。
            /// 
            protected override void Dispose(bool disposing)
            {
                if (disposing)
                {
                    selectionPen?.Dispose();  // 释放画笔
                }
                base.Dispose(disposing);
            }
        }
        /// 
        /// 实现 IDisposable 接口(当前类无额外需要释放的资源,但保留方法以备将来扩展)。
        /// 
        public void Dispose()
        {
            // 无托管资源需要释放,但为了接口完整性保留空方法
        }
    }
}
来源:https://www.jb51.net/program/362911d5t.htm

游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。

同类文章
更多
Java程序在Ubuntu如何运行

Java程序在Ubuntu如何运行

在Ubuntu上运行Ja va程序:一份清晰的实战指南 想在Ubuntu系统上顺利运行Ja va程序?其实过程并不复杂,核心在于准备好Ja va运行环境,并遵循几个关键步骤。下面这份指南,将带你一步步完成从环境搭建到程序执行的完整流程。 第一步:启动终端 所有操作都将在终端(Terminal)中进行

时间:2026-04-27 22:07
Linux与Rust的生态系统如何协同发展

Linux与Rust的生态系统如何协同发展

Linux 与 Rust 生态系统的协同发展 当谈论系统软件的现代化与安全性时,Linux与Rust的结合已经从一个备受瞩目的技术趋势,演变为一条清晰且正在加速的实践路径。两者的协同并非简单的语言替换,而是一场围绕内核、工具链和基础设施的深度整合。那么,这场协同究竟是如何展开的?其背后的节奏与逻辑又

时间:2026-04-27 22:07
如何利用Rust实现Linux系统的自动化运维

如何利用Rust实现Linux系统的自动化运维

利用Rust实现Linux系统的自动化运维 在追求效率与稳定性的Linux系统运维领域,Rust正迅速成为一股不可忽视的技术力量。这门以内存安全和高性能著称的系统编程语言,为构建自动化运维工具提供了全新的解决方案。它不仅能高效处理文件操作、网络配置、服务管理等常规运维任务,更能凭借其独特的并发安全优

时间:2026-04-27 22:06
如何利用Rust提升Linux应用的性能

如何利用Rust提升Linux应用的性能

如何利用Rust为Linux应用注入性能强心剂 你是否在寻求让Linux应用运行更快速、更稳定的方法?Rust作为一门现代系统级编程语言,凭借其卓越的内存安全保证与零成本抽象特性,已成为高性能Linux应用开发与优化的首选工具。本文将深入探讨一系列实用策略,帮助您有效利用Rust提升应用性能。 1

时间:2026-04-27 22:06
如何在Linux上使用Rust编写安全代码

如何在Linux上使用Rust编写安全代码

在Linux上使用Rust编写安全代码 你是否正在寻找一种在Linux系统上开发既高效又安全的系统级软件的方法?Rust语言凭借其卓越的内存安全特性和高性能,已成为开发者的首选。它通过独特的所有权模型和严格的编译时检查,从根本上杜绝了内存泄漏、数据竞争等常见的安全漏洞。本文将为你提供一份完整的指南,

时间:2026-04-27 22:06
热门专题
更多
刀塔传奇破解版无限钻石下载大全 刀塔传奇破解版无限钻石下载大全
洛克王国正式正版手游下载安装大全 洛克王国正式正版手游下载安装大全
思美人手游下载专区 思美人手游下载专区
好玩的阿拉德之怒游戏下载合集 好玩的阿拉德之怒游戏下载合集
不思议迷宫手游下载合集 不思议迷宫手游下载合集
百宝袋汉化组游戏最新合集 百宝袋汉化组游戏最新合集
jsk游戏合集30款游戏大全 jsk游戏合集30款游戏大全
宾果消消消原版下载大全 宾果消消消原版下载大全
  • 日榜
  • 周榜
  • 月榜
热门教程
更多
  • 游戏攻略
  • 安卓教程
  • 苹果教程
  • 电脑教程