C# 实现完整功能的截图控件(4)-完整版
- 摘要:C#实现完整功能的仿QQ截图控件,可以在图片上绘制简单的图形和文字,而且实现简单的撤销功能。这篇文章将介绍完整的截图控件CaptureImageTool的详细实现。
很多人说找不到源代码下载,在这里加个说明吧。(文章标题下 -- 附件列表 里下载)
前面的三篇文章(绘图工具栏、颜色字体选择、快捷菜单)实现了截图控件中需要用到的控件,为实现截图控件CaptureImageTool做好了准备工作,今天就来把整个截图控件的功能全部实现。
这个截图控件需要实现的功能包括:截图、绘制矩形、圆形、箭头、线条、文字,还需要可以撤销绘制步骤、保存图形,把QQ截图拥有的功能基本都实现。先来看看最终的效果,然后再来介绍实现过程。


下面来一步步的介绍怎样实现这个截图控件:
1、截图。截图的原理很简单,网上都很多了,还是介绍一下吧。先利用API把整个屏幕拷贝到一个Image中,然后建立一个无标题栏的窗体,把它的背景图设为这个Image就行了。看看截图的函数:
private Image GetDestopImage()
{
Rectangle rect = Screen.GetBounds(this);
Bitmap bmp = new Bitmap(
rect.Width, rect.Height, PixelFormat.Format32bppArgb);
Graphics g = Graphics.FromImage(bmp);
![]()
IntPtr gHdc = g.GetHdc();
IntPtr deskHandle = NativeMethods.GetDesktopWindow();
![]()
IntPtr dHdc = NativeMethods.GetDC(deskHandle);
NativeMethods.BitBlt(
gHdc,
0,
0,
Width,
Height,
dHdc,
0,
0,
NativeMethods.TernaryRasterOperations.SRCCOPY);
NativeMethods.ReleaseDC(deskHandle, dHdc);
g.ReleaseHdc(gHdc);
return bmp;
}
2、选择截图区域。记录鼠标按下时的坐标,当鼠标移动时得到鼠标所在的坐标,通过这两个坐标就可以得到选择的矩形区域了,通过OnPaint方法画出所选择的区域,当鼠标放开时,就得到最终选取的截图区域了,需要记录下这个区域,下面很多地方需要用到它。
3、调整截图区域的大小和位置。这个实现稍微麻烦些,当鼠标移动的时候,需要判断鼠标是否处于我们设置的调节截图区域大小所在的小矩形内,是的话就改变鼠标指针的样式,提示用户这里按下鼠标可以改变截图区域的大小和位置。当用户在这个地方按下鼠标后,移动鼠标,就可以通过原来记录下来的截图区域和当前鼠标所在的位置调整截图区域的大小了。当放开鼠标后,需要保存这个新的截图区域。
4、实现绘图。利用前面已经实现的绘图工具栏和颜色字体选择控件,可以选择相应的图形进行绘制。简单的说就是根据鼠标的选择区域,绘制相应的图形,实现起来比较复杂,特别是文字和线条的绘制,不是简单的几句话可以介绍出来的,大家还是参照源代码自己体会吧。
5、实现撤销功能。要实现撤销功能,在前面绘图的时候,我们需要记录下每一步绘图完成的时候的动作,撤销绘图就可以返回到上一步,然后重绘。程序中实现了一个简单的操作管理的类OperateManager,可以方便的增加一步操作和移除一步操作。管理类最多纪录1000个操作,当超过1000个时,就会把第一个操作丢掉。看看这个类的完整代码:
internal class OperateManager : IDisposable
{
private List<OperateObject> _operateList;
![]()
private static readonly int MaxOperateCount = 1000;
![]()
public OperateManager()
{
}
![]()
public List<OperateObject> OperateList
{
get
{
if (_operateList == null)
{
_operateList = new List<OperateObject>(100);
}
return _operateList;
}
}
![]()
public int OperateCount
{
get { return OperateList.Count; }
}
![]()
public void AddOperate(
OperateType operateType,
Color color,
object data)
{
OperateObject obj = new OperateObject(
operateType, color, data);
if (OperateList.Count > MaxOperateCount)
{
OperateList.RemoveAt(0);
}
OperateList.Add(obj);
}
![]()
public bool RedoOperate()
{
if (OperateList.Count > 0)
{
OperateList.RemoveAt(OperateList.Count - 1);
return true;
}
return false;
}
![]()
public void Clear()
{
OperateList.Clear();
}
![]()
IDisposable 成员
}
6、完成和保存截图。双击截图选择区域或者通过绘图工具栏的完成按钮和快捷菜单的完成项。当完成截图时,把选择的图形和所进行的画图操作全部绘制到一个新的Image中,把这个Image返回给用户。绘制通过下面这个函数实现:
private void DrawLastImage()
{
using (Bitmap allBmp = new Bitmap(
Width, Height, PixelFormat.Format32bppArgb))
{
using (Graphics allGraphics = Graphics.FromImage(allBmp))
{
allGraphics.InterpolationMode =
InterpolationMode.HighQualityBicubic;
allGraphics.SmoothingMode = SmoothingMode.AntiAlias;
allGraphics.DrawImage(
BackgroundImage,
Point.Empty);
DrawOperate(allGraphics);
allGraphics.Flush();
![]()
Bitmap bmp = new Bitmap(
SelectImageRect.Width,
SelectImageRect.Height,
PixelFormat.Format32bppArgb);
Graphics g = Graphics.FromImage(bmp);
g.DrawImage(
allBmp,
0,
0,
SelectImageRect,
GraphicsUnit.Pixel);
![]()
g.Flush();
g.Dispose();
_image = bmp;
}
}
}
7、取消截图。实现了跟QQ截图类似的取消截图方法,鼠标右键双击非选择的截图区域,或者点击绘图工具栏的退出按钮和快捷菜单的退出项。
8、改变颜色风格。实现了一个颜色表类CaptureImageToolColorTable,只需要继承这个类,相应的改变颜色,然后把CaptureImageTool的ColorTable设为它,就可以得到自己的不同的颜色风格了。看看颜色表类:
public class CaptureImageToolColorTable
{
private static readonly Color _borderColor = Color.FromArgb(65, 173, 236);
private static readonly Color _backColorNormal = Color.FromArgb(229, 243, 251);
private static readonly Color _backColorHover = Color.FromArgb(65, 173, 236);
private static readonly Color _backColorPressed = Color.FromArgb(24, 142, 206);
private static readonly Color _foreColor = Color.FromArgb(12, 83, 124);
![]()
public CaptureImageToolColorTable() { }
![]()
public virtual Color BorderColor
{
get { return _borderColor; }
}
![]()
public virtual Color BackColorNormal
{
get { return _backColorNormal; }
}
![]()
public virtual Color BackColorHover
{
get { return _backColorHover; }
}
![]()
public virtual Color BackColorPressed
{
get { return _backColorPressed; }
}
![]()
public virtual Color ForeColor
{
get { return _foreColor; }
}
}
9、改变鼠标样式。通过设置CaptureImageTool的SelectCursor和DrawCursor属性,可以改变截图和绘图时的鼠标样式,程序还提供了一个鼠标管理类CursorManager,你可以用它来设置自己的不同的鼠标,看看源代码:
internal class CursorManager
{
public static readonly Cursor Arrow =
CreateCursor("Cursors//Arrow.cur");
![]()
public static readonly Cursor Cross =
CreateCursor("Cursors//Cross.cur");
![]()
public static readonly Cursor ArrowNew =
CreateCursor("Cursors//ArrowNew.cur");
![]()
public static readonly Cursor CrossNew =
CreateCursor("Cursors//CrossNew.cur");
![]()
private CursorManager() { }
![]()
public Cursor CreateCursor(Bitmap cursor, Point hotPoint)
{
int hotX = hotPoint.X;
int hotY = hotPoint.Y;
using (Bitmap cursorBmp = new Bitmap(
cursor.Width * 2 - hotX,
cursor.Height * 2 - hotY,
PixelFormat.Format32bppArgb))
{
using (Graphics g = Graphics.FromImage(cursorBmp))
{
g.Clear(Color.FromArgb(0, 0, 0, 0));
g.DrawImage(
cursor,
cursor.Width - hotX,
cursor.Height - hotY,
cursor.Width,
cursor.Height);
g.Flush();
}
return new Cursor(cursorBmp.GetHicon());
}
}
![]()
public static Cursor CreateCursor(string fileName)
{
IntPtr cursorHandle = LoadCursorFromFile(fileName);
return new Cursor(cursorHandle);
}
![]()
[DllImport("user32.dll")]
private static extern IntPtr LoadCursorFromFile(string fileName);
![]()
[DllImport("user32.dll")]
private static extern uint DestroyCursor(IntPtr cursorHandle);
}
还有一些实现的具体细节就不一一介绍了,这里将提供完整源代码下载,大家从代码中了解吧,由于时间有限,都没来得及写注释,但是代码的命名还算规范吧,相信认真阅读是能看懂的。
声明:
本文版权归作者和CS 程序员之窗所有,欢迎转载,转载必须保留以下版权信息,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
作者:Starts_2000
出处:CS 程序员之窗 http://www.csharpwin.com。
你可以免费使用或修改提供的源代码,但请保留源代码中的版权信息,详情请查看:
CS程序员之窗开源协议 http://www.csharpwin.com/csol.html。


Rectangle rect
}
}