前言

通过创建客制化组件(继承pictureBox),新增属性和构造方法,实现屏幕截图时需要用到的功能点。再通过监控鼠标按下、移动和释放,来获取起始点区域。最后通过操作BMP图像,实现截图的新增、修改和保存功能。

核心点

  • 组件的创建(重写)
  • 鼠标监控事件
  • BMP图像重绘

核心代码

     /// <summary>
/// 重写图片控件
/// </summary>
[Serializable]
public partial class HexPictureBox : PictureBox
{
#region 属性和构造函数 public HexPictureBox()
{
InitializeComponent();
} private bool startDraw = false; public bool StartDraw
{
get { return startDraw; } set { startDraw = value; }
} private List<HLine> lineHistory = new List<HLine>(); public List<HLine> LineHistory
{
get { return lineHistory; } private set { lineHistory = value; }
} private HLine curLine = new HLine() { LineColor = Color.White, LineWidth = , PointList = new List<Point>() }; /// <summary>
/// 当前绘制线
/// </summary>
public HLine CurLine
{
get { return curLine; } private set { curLine = value; }
} private HRectangle curRect = new HRectangle() { LineColor = Color.White, LineWidth = , Start = new Point(, ), End = new Point(, ) }; /// <summary>
/// 当前需要绘制的矩形
/// </summary>
public HRectangle CurRect
{
get { return curRect; } set { curRect = value; }
} private List<HRectangle> rectHistory = new List<HRectangle>(); public List<HRectangle> RectHistory
{
get { return rectHistory; } private set { rectHistory = value; }
} private DrawType drawTypes = DrawType.None; public DrawType DrawTypes
{
get { return drawTypes; } set { drawTypes = value; }
} #endregion #region 绘制功能 protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
Graphics g = pe.Graphics;
DrawHistory(g);
//绘制当前线
if (startDraw && this.curLine.PointList != null && this.curLine.PointList.Count > )
{
DrawLine(g, this.curLine);
}
if (startDraw && this.curRect.Start != null && this.curRect.End != null && this.curRect.Start != this.curRect.End)
{
DrawRectangle(g, this.curRect);
}
} public void DrawHistory(Graphics g)
{
//绘制线历史记录
if (LineHistory != null)
{
foreach (HLine lh in LineHistory)
{
if (lh.PointList.Count > )
{
DrawLine(g, lh);
}
}
}
//绘制矩形历史记录
if (RectHistory != null)
{
foreach (HRectangle lh in RectHistory)
{
if (lh.Start != null && lh.End != null && lh.Start != lh.End)
{
DrawRectangle(g, lh);
}
}
}
} /// <summary>
/// 绘制线
/// </summary>
/// <param name="g"></param>
/// <param name="line"></param>
private void DrawLine(Graphics g, HLine line)
{
g.SmoothingMode = SmoothingMode.AntiAlias;
using (Pen p = new Pen(line.LineColor, line.LineWidth))
{
//设置起止点线帽
p.StartCap = LineCap.Round;
p.EndCap = LineCap.Round; //设置连续两段的联接样式
p.LineJoin = LineJoin.Round;
g.DrawCurve(p, line.PointList.ToArray()); //画平滑曲线
}
} /// <summary>
/// 绘制矩形
/// </summary>
/// <param name="g"></param>
/// <param name="rect"></param>
private void DrawRectangle(Graphics g, HRectangle rect)
{
g.SmoothingMode = SmoothingMode.AntiAlias;
using (Pen p = new Pen(rect.LineColor, rect.LineWidth))
{
//设置起止点线帽
p.StartCap = LineCap.Round;
p.EndCap = LineCap.Round; //设置连续两段的联接样式
p.LineJoin = LineJoin.Round;
g.DrawRectangle(p, rect.Start.X, rect.Start.Y, rect.End.X - rect.Start.X, rect.End.Y - rect.Start.Y); //画平滑曲线
}
} public void Earser(Point p0)
{
for (int i = lineHistory.Count - ; i >= ; i--)
{
HLine line = lineHistory[i];
bool flag = false;
foreach (Point p1 in line.PointList)
{
double distance = GetDistance(p0, p1);
if (Math.Abs(distance) < )
{
//需要删除
flag = true;
break;
} }
if (flag)
{
lineHistory.RemoveAt(i);
}
}
//擦除矩形
for (int i = rectHistory.Count - ; i >= ; i--)
{
HRectangle rect = rectHistory[i]; if (p0.X > rect.Start.X && p0.X < rect.End.X && p0.Y > rect.Start.Y && p0.Y < rect.End.Y)
{ rectHistory.RemoveAt(i);
}
}
} /// <summary>
/// 获取两点之间的距离
/// </summary>
/// <param name="p0"></param>
/// <param name="p1"></param>
/// <returns></returns>
private double GetDistance(Point p0, Point p1)
{
return Math.Sqrt(Math.Pow((p0.X - p1.X), ) + Math.Pow((p0.Y - p1.Y), ));
} #endregion #region 鼠标事件响应 /// <summary>
/// 鼠标移动时设置点
/// </summary>
/// <param name="p"></param>
public void SetPointAndRefresh(Point p)
{
if (this.drawTypes == DrawType.Line)
{
this.curLine.PointList.Add(p);
}
if (this.drawTypes == DrawType.Rect)
{
this.curRect.End = p;
}
this.Refresh(); } public void OnMouseDown(Point p)
{
if (this.DrawTypes == DrawType.Line)
{
this.CurLine.PointList.Clear();
this.CurLine.PointList.Add(p);
}
if (this.DrawTypes == DrawType.Rect)
{
this.CurRect.Start = p;
}
} public void OnMouseUp(Point p)
{
if (this.DrawTypes == DrawType.Line)
{
//右键起来时,停止绘图,并写入历史记录
Point[] pCopy = this.CurLine.PointList.ToArray();
List<Point> lstPoint = new List<Point>();
lstPoint.AddRange(pCopy);
this.LineHistory.Add(new HLine() { LineColor = this.CurLine.LineColor, LineWidth = this.CurLine.LineWidth, PointList = lstPoint });
this.CurLine.PointList.Clear();
}
if (this.DrawTypes == DrawType.Rect)
{
this.RectHistory.Add(new HRectangle() { LineColor = this.CurRect.LineColor, LineWidth = this.CurRect.LineWidth, Start = this.CurRect.Start, End = this.CurRect.End });
this.CurRect.Start = new Point(, );
this.CurRect.End = new Point(, );
}
if (this.DrawTypes == DrawType.Earser)
{
//如果是橡皮擦功能
this.Earser(p);
this.Refresh();
}
} #endregion #region 初始设置 public void SetPen(Color c)
{
this.DrawTypes = DrawType.Line;
this.CurLine.LineWidth = ;
this.CurLine.LineColor = c;
//通过图片的句柄获取指针
this.Cursor = new Cursor(global::ScreenShot.Properties.Resources.pen.GetHicon());
this.StartDraw = true;
} public void SetLightPen()
{
this.StartDraw = true;
this.CurLine.LineWidth = ;
this.DrawTypes = DrawType.Line;
this.Cursor = new Cursor(global::ScreenShot.Properties.Resources.lightpen.GetHicon());
this.CurLine.LineColor = Color.FromArgb(, Color.Yellow);
} public void SetRectangle()
{
this.StartDraw = true;
this.CurRect.LineWidth = ;
this.DrawTypes = DrawType.Rect;
this.CurRect.LineColor = Color.Red;
this.Cursor = Cursors.Cross;
} public void SetEarser()
{
this.DrawTypes = DrawType.Earser;//橡皮檫
this.Cursor = new Cursor(global::ScreenShot.Properties.Resources.easer1.GetHicon());
} public void SetDefault()
{
this.Cursor = Cursors.Default;
this.StartDraw = false;
} #endregion
}

实现效果

作者:Jeremy.Wu
  出处:https://www.cnblogs.com/jeremywucnblog/

  本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

C# - VS2019通过重写pictureBox实现简单的桌面截图功能的更多相关文章

  1. VC++ 实现简单的桌面截图

    使用了EasyX图像库,使用方法请参考:VC++ 制作一个简易的控制台时钟应用 简单的桌面截图代码: ///////////////////////////////////////////////// ...

  2. CSharpGL(24)用ComputeShader实现一个简单的图像边缘检测功能

    CSharpGL(24)用ComputeShader实现一个简单的图像边缘检测功能 效果图 这是红宝书里的例子,在这个例子中,下述功能全部登场,因此这个例子可作为使用Compute Shader的典型 ...

  3. 完成一段简单的Python程序,用于实现一个简单的加减乘除计算器功能

    #!/bin/usr/env python#coding=utf-8'''完成一段简单的Python程序,用于实现一个简单的加减乘除计算器功能'''try: a=int(raw_input(" ...

  4. Selenium + PhantomJS + python 简单实现爬虫的功能

    Selenium 一.简介 selenium是一个用于Web应用自动化程序测试的工具,测试直接运行在浏览器中,就像真正的用户在操作一样 selenium2支持通过驱动真实浏览器(FirfoxDrive ...

  5. iOS开发——使用技术OC篇&简单九宫格锁屏功能的实现与封装

    简单九宫格锁屏功能的实现与封装 首先来看看最后的实现界面. 在这开始看下面的内容之前希望你能先大概思考活着回顾一下如果 你会怎么做,只要知道大概的思路就可以. 由于iphone5指纹解锁的实现是的这个 ...

  6. 简单 TCP/IP 服务功能

    本主题使用每台 Windows 计算机上提供的 Echo 和 Quote of the Day 服务.在所有 Windows 版本中都提供了简单 TCP/IP 服务功能.该功能会提供了以下服务:Cha ...

  7. Python django实现简单的邮件系统发送邮件功能

    Python django实现简单的邮件系统发送邮件功能 本文实例讲述了Python django实现简单的邮件系统发送邮件功能. django邮件系统 Django发送邮件官方中文文档 总结如下: ...

  8. Netty学习笔记(四) 简单的聊天室功能之服务端开发

    前面三个章节,我们使用了Netty实现了DISCARD丢弃服务和回复以及自定义编码解码,这篇博客,我们要用Netty实现简单的聊天室功能. Ps: 突然想起来大学里面有个课程实训,给予UDP还是TCP ...

  9. 基于PHP实现一个简单的在线聊天功能(轮询ajax )

    基于PHP实现一个简单的在线聊天功能(轮询ajax ) 一.总结 1.用的轮询ajax 二.基于PHP实现一个简单的在线聊天功能 一直很想试着做一做这个有意思的功能,感觉复杂的不是数据交互和表结构,麻 ...

随机推荐

  1. 2019阿里天猫团队Java高级工程师面试题之第三面

    2019阿里天猫团队Java高级工程师面试题之第一面 2019阿里天猫团队Java高级工程师面试题之第二面 1.说说MySQL的锁并发?加锁的机制是什么? https://www.cnblogs.co ...

  2. ReactNative: 使用尺寸类Dimensions获取屏幕尺寸

    一.简介 在前面创建使用组件时,虽然使用的都是伸缩盒子布局,但是很少使用宽高来进行绝对定位.在iOS中可以通过UIScreen控件获取当前屏幕的宽高,同样地,在RN中提供了一个尺寸组件Dimensio ...

  3. 深入浅出14个Java并发容器

    前言 不考虑多线程并发的情况下,容器类一般使用ArrayList.HashMap等线程不安全的类,效率更高.在并发场景下,常会用到ConcurrentHashMap.ArrayBlockingQueu ...

  4. Web前端基础(10):JavaScript(四)

    1. 伪数组arguments arguments代表的是实参.有个讲究的地方是:arguments只在函数中使用. 1.1 返回参数个数 返回函数实参的个数:arguments.length 例子: ...

  5. 大数据相关概念和hdfs

    大数据 概述 大数据是新处理模式才能具备更多的决策力,洞察力,流程优化能力,来适应海量高增长率,多样化的数据资产. 大数据面临的问题 怎么存储海量数据(kb,mb,gb,tb,pb,eb,zb) 怎么 ...

  6. mysql定时任务(event事件)

    1.事件简介 事件(event)是MySQL在相应的时刻调用的过程式数据库对象.一个事件可调用一次,也可周期性的启动,它由一个特定的线程来管理的,也就是所谓的“事件调度器”. 事件和触发器类似,都是在 ...

  7. ORM优化查询、choices参数

    目录 ORM查询优化 only与defer select_related和prefetch_related MTV与MVC模型 choices参数 ORM查询优化 only与defer res = m ...

  8. acwing 47. 二叉树中和为某一值的路径

    地址 https://www.acwing.com/problem/content/description/45/ 输入一棵二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径. 从树的根结 ...

  9. 一天两道PAT(1)

    其实是从昨天开始刷的,备战一下PAT(乙级,菜鸡不解释,希望几个月下来能有长进吧),做了一下,发现自己的算法功底好差啊..... 先上题目 1. 字符串中必须仅有P, A, T这三种字符,不可以包含其 ...

  10. vue中简单的数据请求 fetch axios

    fetch 不需要额外的安装什么东西,直接就可以使用 fetch(url, { method:'post', headers: { 'Content-Type': 'application/x-www ...