C#实现截图工具小项目
作者:*八步赶蝉* 发布时间:2023-10-02 08:20:03
标签:C#,截图
本文实例为大家分享了C#实现截图工具小项目的具体代码,供大家参考,具体内容如下
1.起因
一直用的截图是qq的截图,所以想要实现一个简单点的截图,为了方便。
2.思路
讲一下实现流程。
1、主窗体,上有截图按钮,点击进入截图窗体
2、在截图窗体中,背景设置为全屏幕的截图图片,无边框,窗体最大化,这时你看到的就是一张屏幕图,其实是一个窗体,然后我们将在这个窗体中截取图片,其实主要就是画板Graphics的使用,截取完之后图片将保存到剪切板。
3.代码
热键注册类 HotKey.cs
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace test {
/// <summary>
/// 热键类
/// </summary>
public class HotKey {
/// <summary>
/// 如果函数执行成功,返回值不为0,如果执行失败,返回值为0
/// </summary>
/// <returns></returns>
[DllImport("user32.dll", SetLastError = true)]
public static extern bool RegisterHotKey(
IntPtr hWnd, // 窗口的句柄, 当热键按下时,会产生WM_HOTKEY信息,该信息该会发送该窗口句柄
int id, // 定义热键ID,属于唯一标识热键的作用
uint fsModifiers, // 热键只有在按下Alt、 Ctrl、Shift、Windows等键时才会生效,即才会产生WM_HOTKEY信息
Keys vk // 虚拟键,即按了Alt+Ctrl+ X ,X就是代表虚拟键
);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool UnregisterHotKey(
IntPtr hWnd, // 窗口句柄
int id // 要取消热键的ID
);
}
}
主窗体 Form1.cs
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Threading;
using System.Timers;
using System.Windows.Forms;
namespace test {
public partial class Form1 : Form {
[Flags]
public enum KeyModifiers { //定义热键值字符串(热键值是系统规定的,不能改变)
None = 0,
Alt = 1,
Ctrl = 2,
Shift = 4,
WindowsKey = 8
}
public Form1() {
InitializeComponent();
}
//窗体加载时-注册快捷键
private void Form1_Load(object sender, EventArgs e) {
uint ctrlHotKey = (uint)(KeyModifiers.Alt | KeyModifiers.Ctrl);
// 注册热键为Alt+Ctrl+A, "100"为唯一标识热键
HotKey.RegisterHotKey(Handle,100,ctrlHotKey,Keys.A);
}
//截图按钮
private void button1_Click(object sender, EventArgs e) {
if (this.WindowState != FormWindowState.Minimized) {
this.WindowState = FormWindowState.Minimized;
Thread.Sleep(200);
}
int swidth = Screen.PrimaryScreen.Bounds.Width;
int sheight = Screen.PrimaryScreen.Bounds.Height;
Bitmap btm = new Bitmap(swidth,sheight); //空图与屏幕同大小
Graphics g = Graphics.FromImage(btm); //空图的画板
g.CopyFromScreen(new Point(0,0),new Point(0,0),new Size(swidth,sheight)); //将屏幕内容复制到空图
Cutter cutter = new Cutter(btm); //传送截图
cutter.FormBorderStyle = FormBorderStyle.None; //截图全屏,无边框
cutter.BackgroundImage = btm; //新的窗体截图做背景
cutter.Show();
}
private void tiaoZ(object sender, ElapsedEventArgs e) {
}
//窗体关闭-取消热键
private void Form1_FormClosing(object sender, FormClosingEventArgs e) {
HotKey.UnregisterHotKey(Handle,100);
}
//快捷键按下执行的事件
private void GlobalKeyProcess() {
this.WindowState = FormWindowState.Minimized;
Thread.Sleep(200);
button1.PerformClick();
}
//重写。监视系统消息,调用对应方法
protected override void WndProc(ref Message m) {
const int WM_HOTKEY = 0x0312;
//如果m.Msg的值为0x0312(我也不知道为什么是0x0312)那么表示用户按下了热键
switch (m.Msg) {
case WM_HOTKEY:
if (m.WParam.ToString().Equals("100")) {
GlobalKeyProcess();
}
//todo 其它热键
break;
}
// 将系统消息传递自父类的WndProc
base.WndProc(ref m);
}
}
}
截图窗体-核心 Cutter.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace test {
public partial class Cutter : Form {
Bitmap screenBtmp = null; //电脑屏幕的截图
public Cutter(Bitmap btm) {
InitializeComponent();
screenBtmp = btm;
}
//鼠标右键退出
private void Cutter_MouseClick(object sender, MouseEventArgs e) {
if (e.Button == MouseButtons.Right) {
this.DialogResult = DialogResult.OK;
this.Close();
}
}
bool CatchStart = false; //自由截图开始
Point downPoint; //初始点
//鼠标左键按下-开始自由截图
private void Cutter_MouseDown(object sender, MouseEventArgs e) {
if (e.Button == MouseButtons.Left) {
if (!CatchStart) {
CatchStart = true;
downPoint = new Point(e.X,e.Y); //初始点
}
}
}
Rectangle catchRec;//存放截取范围
//鼠标移动-绘制自由截图路径
private void Cutter_MouseMove(object sender, MouseEventArgs e) { //路径绘制,核心
if (CatchStart) {
//
//二次缓冲
//不是直接在控件的背景画板上进行绘制鼠标移动路径,那样会造成绘制很多路径,因为前面绘制的路径还在
//而是在内存中每移动一次鼠标就创建一张和屏幕截图一样的新BImtap,在这个Bitmap中绘制鼠标移动路径
//然后在窗体背景画板上,绘制这个新的Bitmap,这样就不会造成绘制很多路径,因为每次都绘制了全新的Bitmao
//但是这样做的话,因为鼠标移动的次数是大量的,所以在内存中会创建大量的Bitmap会造成内存消耗严重,所以每次移动绘制完后,
//需要释放Dispose() 画板,画笔,Bitmap资源。
//
Bitmap copyBtmp = (Bitmap)screenBtmp.Clone(); //创建新的,在其上绘制路径
//左上角
Point firstP = new Point(downPoint.X,downPoint.Y);
//新建画板,画笔
Graphics g = Graphics.FromImage(copyBtmp);
Pen p = new Pen(Color.Red,1);
//计算路径范围
int width = Math.Abs(e.X - downPoint.X);
int height = Math.Abs(e.Y - downPoint.Y);
if (e.X < downPoint.X) {
firstP.X = e.X;
}
if (e.Y < downPoint.Y) {
firstP.Y = e.Y;
}
//绘制路径
catchRec = new Rectangle(firstP,new Size(width,height));
//将路径绘制在新的BItmap上,之后要释放
g.DrawRectangle(p, catchRec);
g.Dispose();
p.Dispose();
//窗体背景画板
Graphics gf = this.CreateGraphics();
//将新图绘制在窗体的画板上 -- 自由截图-路径绘制处,其实还是一张和屏幕同样大小的图片,只不过上面有红色的选择路径
gf.DrawImage(copyBtmp,new Point(0,0));
gf.Dispose();
//释放内存Bimtap
copyBtmp.Dispose();
}
}
bool catchFinished = false; //自由截图结束标志
//鼠标左键弹起-结束自由截图
private void Cutter_MouseUp(object sender, MouseEventArgs e) {
if (e.Button == MouseButtons.Left) {
if (CatchStart) {
CatchStart = false;
catchFinished = true;
}
}
}
//鼠标左键双击,保存自由截取的图片
private void Cutter_MouseDoubleClick(object sender, MouseEventArgs e) {
if((e.Button == MouseButtons.Left) && catchFinished){
//创建用户截取的范围大小的空图
Bitmap catchBtmp = new Bitmap(catchRec.Width,catchRec.Height);
Graphics g = Graphics.FromImage(catchBtmp);
//在原始的屏幕截图ScreenBitmap上 截取 用户选择范围大小的区域 绘制到上面的空图
//绘制完后,这个空图就是我们想要的截取的图片
//参数1 原图
//参数2 在空图上绘制的范围区域
//参数3 原图的截取范围
//参数4 度量单位
g.DrawImage(screenBtmp,new Rectangle(0,0,catchRec.Width,catchRec.Height),catchRec,GraphicsUnit.Pixel);
//将自由截取的图片保存到剪切板中
Clipboard.Clear();
Clipboard.SetImage(catchBtmp);
g.Dispose();
catchFinished = false;
this.BackgroundImage = screenBtmp;
catchBtmp.Dispose();
this.DialogResult = DialogResult.OK;
this.Close();
}
}
}
}
来源:https://blog.csdn.net/qq_38261174/article/details/85274325


猜你喜欢
- 本文实例讲述了java继承中的构造方法。分享给大家供大家参考。具体如下:继承中的构造方法: 1、子类的构造过程中必须调用其基类的构造方法。2
- Android CheckBox中设置padding无效解决办法CheckBox使用本地图片资源CheckBox是Android中用的比较多
- SpringBoot默认使用HikariDataSource数据源定义数据源:存储了所有建立数据库连接的信息。通过提供正确的数据源名称,你可
- 目录1.说明2.先来说下@FunctionalInterface3. 下面来讲讲这个 "::"是干嘛的4. 建立一个Pe
- 概述还没玩过Spring Boot,现在越来越多的公司在用了,不得不学习了。本篇是Spring Boot的开篇,简单介绍一下如何创建一个Sp
- 当使用struts2的validation.xml进行验证时,需要注意如下几个方面:I.首先应该注意validation.xml的名字,一定
- 静态变量静态变量位于栈上,它是一个全局变量,在编译期就已经生成。public class Cow{public static int cou
- java文件的多线程断点续传大致原理,供大家参考,具体内容如下谈到文件断点续传那么就离不开java.io.RandomAcessFile H
- 目录MotionEventViewViewGroup事件拦截寻找目标视图,分发ACTION_DOWN分发除ACTION_DOWN外的其他事件
- 先看效果图一、申请成为百度开发者,获得使用地图API接口的权限,获取(AK)码。1.打开百度地图开放平台打开网址:http://lbsyun
- 目录1、概念相关1.1、概念1.2、解决了什么:1.3、场景:2、简单实现2.1 代码3. netty中的责任链模式4、思考本文先介绍了责任
- 本文实例为大家分享了java多线程之铁路售票系统的具体代码,供大家参考,具体内容如下问题:铁路售票,一共100张,通过四个窗口卖完。要求:分
- 一、默认异常处理机制默认情况下,SpringBoot 提供 /error 请求,来处理所有异常的。1.浏览器客户端,请求头里的属性是Acce
- 前言StringJoiner是Java里1.8新增的类,主要是帮助我们把一个列表拼接字符串, 或许有一部分人没有接触过. 所以本文将从使用例
- 预加载bean在springBoot启动过程中就完成创建加载在AbstractApplicationContext的refresh方法中//
- Android Studio 是谷歌基于IntelliJ IDEA开发的安卓开发工具,有点类似 EcliPSe ADT,Android St
- 经常有同学问到,使用Android能不能开发游戏呢?能开发那些游戏呢?由于操作系统和开发语言局限,一般开发安卓手机游戏,我们很少使用其自带语
- 模糊查询Spring Data Jpa的使用可以减少开发者对sql语句的编写,甚至完全不需要编写sql语句。但是,开发过程中总会遇到各种复杂
- 卫星菜单 ArcMenu 相信大家接触安卓,从新手到入门的过渡,就应该会了解到卫星菜单、抽屉、Xutils、Coolmenu、一些大神封装好
- 1、mybatis-plus @DS实现动态切换数据源原理首先mybatis-plus使用com.baomidou.dynamic.data