首页 游戏 软件 资讯 排行榜 专题
首页
编程语言
使用C#自制一个截屏工具

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

热心网友
85
转载
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。

最新APP

宝宝过生日
宝宝过生日
应用辅助 04-07
台球世界
台球世界
体育竞技 04-07
解绳子
解绳子
休闲益智 04-07
骑兵冲突
骑兵冲突
棋牌策略 04-07
三国真龙传
三国真龙传
角色扮演 04-07

热门推荐

我国刀具市场发展调研报告
办公文书
我国刀具市场发展调研报告

我国刀具市场发展调研报告 在当今制造业持续升级的背景下,市场调研报告的重要性日益凸显。一份结构清晰、数据翔实的报告,能为决策提供关键参考。以下这份关于我国刀具市场的调研报告,旨在梳理现状、剖析问题,并为未来发展提供借鉴。 当前,国内刀具年销售额约为145亿元,其中硬质合金刀具占比不足25%。这一比例

热心网友
04.27
国内首份空净市场调研报告
办公文书
国内首份空净市场调研报告

国内首份空净市场调研报告 在公众健康意识日益增强的今天,市场报告的重要性不言而喻。一份结构清晰、数据翔实的报告,能为行业描绘出精准的航图。那么,一份优秀的市场调研报告究竟该如何呈现?近期发布的这份国内空气净化器行业蓝皮书,或许能提供一个范本。 市场增长的势头有多强劲?数据显示,国内空气净化器市场正驶

热心网友
04.27
水利工程供水管理调研报告
办公文书
水利工程供水管理调研报告

水利工程供水管理调研报告 在各类报告日益成为工作常态的今天,撰写一份扎实的调研报告,关键在于厘清现状、找准问题、提出思路。这份关于水利工程供水管理的报告,旨在系统梳理情况,为后续决策提供参考。 一、基本情况 横跨区域的**水库及八座枢纽拦河闸,构成了**运河流域防洪与兴利供水的骨干工程体系。自投入运

热心网友
04.27
财产保全申请书范本
办公文书
财产保全申请书范本

财产保全申请书范本 一份规范的财产保全申请书,是启动财产保全程序的关键文书。其核心在于清晰、准确地列明各方信息、诉求与依据。通常,申请书的结构是固定的,但具体内容需要根据案件事实来填充。下面,我们通过几个典型的范本来拆解其中的要点。 篇一:通用格式范本 首先来看一个通用模板。这个模板清晰地勾勒出了申

热心网友
04.27
暑假大学生防台风社会实践调研报告范文
办公文书
暑假大学生防台风社会实践调研报告范文

“防台抗台”活动由学院的积极分子组成,他们踊跃报名,利用暑期时间奉献自己的青春,为社会尽一份力量。 带队的学院分团委书记吕老师点出了活动的深层价值:这不仅是一次能力锻炼,更是学生认识社会、融入社会并最终回馈社会的关键一步。经过这番历练,团队友谊愈发坚固,协作精神显著增强,感恩之心也油然而生。 青春洋

热心网友
04.27