上面说了这么多的(可能说的有点多),主要是让大家明白,系统学习C#基础知识是很有必要的,系统学习完C#基础知识之后就是代码量的积累了,也就是自己做一些小工具,积累到一定代码量之后,就可以尝试写写一些大的项目或开源项目等,所以在后面的系列中将会分享一些具体工具的开发,同时这也是我自己的一个学习的计划,这里分享给大家希望对一些迷茫的朋友有所帮助。如果你现在还没有明确或更好地目标,并且也是从事.NET工作或学习的朋友,那就和我一起静下心来学编程,下面是我的一个学习方向图(可能多少有点偏差,相信大致意图大家可以明白):

啰嗦了这么多,下面就具体介绍下实现截图工具的实现思路。
为了让大家更清楚地知道如何去实现自己的截图工具,首先我来描述下截图的一个过程——我们使用QQ的截图工具和Windows 自带的截图工具都可以发现,当我们点击QQ窗体中的截图按钮时,此时我们将看到一个全屏图片,然后我们可以在其上截图,当鼠标左键按下时,即代表开始截图,并我们可以移动鼠标来改变截图的大小,鼠标弹起时即代表结束截图,此时我们可以双击矩形区域完全截图,并且可以通过粘贴操作把截取的图片粘贴到聊天窗口的发送区,鼠标右键点击则是退出截图。这样我们截图的过程描述就是这样的,从这个描述中我们就可以抽象出实现我们截图工具的思路来:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<code>// 通过Graphics的CopyFromScreen方法把全屏图片的拷贝到我们定义好的一个和屏幕大小相同的空白图片中,</code>
<code> </code><code>// 拷贝完成之后,CatchBmp就是全屏图片的拷贝了,然后指定为截图窗体背景图片就好了。</code>
<code> </code><code>// 新建一个和屏幕大小相同的图片</code>
<code> </code><code>Bitmap CatchBmp = </code><code>new</code> <code>Bitmap(Screen.AllScreens[0].Bounds.Width, Screen.AllScreens[0].Bounds.Height);</code>
<code> </code>
<code> </code><code>// 创建一个画板,让我们可以在画板上画图</code>
<code> </code><code>// 这个画板也就是和屏幕大小一样大的图片</code>
<code> </code><code>// 我们可以通过Graphics这个类在这个空白图片上画图</code>
<code> </code><code>Graphics g = Graphics.FromImage(CatchBmp);</code>
<code> </code><code>// 把屏幕图片拷贝到我们创建的空白图片 CatchBmp中</code>
<code> </code><code>g.CopyFromScreen(</code><code>new</code> <code>Point(0, 0), </code><code>new</code> <code>Point(0, 0), </code><code>new</code> <code>Size(Screen.AllScreens[0].Bounds.Width, Screen.AllScreens[0].Bounds.Height));</code>
<code> </code><code>// 创建截图窗体</code>
<code> </code><code>cutter = </code><code>new</code> <code>Cutter();</code>
<code> </code><code>// 指示窗体的背景图片为屏幕图片</code>
<code> </code><code>cutter.BackgroundImage = CatchBmp;</code>
4. 从 “当鼠标左键按下时,即代表开始截图,并我们可以移动鼠标来改变截图的大小,鼠标弹起时即代表结束截图,此时我们可以双击矩形区域完全截图,并且可以通过粘贴操作把截取的图片粘贴到聊天窗口的发送区,鼠标右键点击则是退出截图”这些描述中可以抽象为——鼠标的移动,按下,弹起等操作,在程序角度来说,也就是实现截图窗体的MouseMove事件(对应于鼠标移动),MouseDown事件(对应于鼠标左键按下),MouseClick事件(对应于鼠标右键结束截图)、MouseUp(对应于鼠标弹起结束截图)和MouseDoubleClick(鼠标双击矩形区域完全截图,并可以通过粘贴操作把截取的图片粘贴到聊天窗口的发送区,既然可以进行粘贴操作来获得截取图片,所以必须在该事件中对剪切板设置截图图片),3和4的分析过程也是截图功能的核心实现,对应于下面的代码(代码中有详细解释,并且大家理解的时候可以结合3和4的分析):
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
<code>/// <summary></code>
<code> </code><code>/// 鼠标右键点击结束截图</code>
<code> </code><code>/// </summary></code>
<code> </code><code>/// <param name="sender"></param></code>
<code> </code><code>/// <param name="e"></param></code>
<code> </code><code>private</code> <code>void</code> <code>Cutter_MouseClick(</code><code>object</code> <code>sender, MouseEventArgs e)</code>
<code> </code><code>{</code>
<code> </code><code>if</code> <code>(e.Button == MouseButtons.Right)</code>
<code> </code><code>{</code>
<code> </code><code>this</code><code>.DialogResult = DialogResult.OK;</code>
<code> </code><code>this</code><code>.Close();</code>
<code> </code><code>}</code>
<code> </code><code>}</code>
<code> </code><code>/// <summary></code>
<code> </code><code>/// 鼠标按下事件处理程序</code>
<code> </code><code>private</code> <code>void</code> <code>Cutter_MouseDown(</code><code>object</code> <code>sender, MouseEventArgs e)</code>
<code> </code><code>// 鼠标左键按下是开始画图,也就是截图</code>
<code> </code><code>if</code> <code>(e.Button == MouseButtons.Left)</code>
<code> </code><code>// 如果捕捉没有开始</code>
<code> </code><code>if</code> <code>(!CatchStart)</code>
<code> </code><code>{ </code>
<code> </code><code>CatchStart = </code><code>true</code><code>;</code>
<code> </code><code>// 保存此时鼠标按下坐标</code>
<code> </code><code>DownPoint = </code><code>new</code> <code>Point(e.X, e.Y);</code>
<code> </code><code>}</code>
<code> </code><code>/// 鼠标移动事件处理程序,即用户改变截图大小的处理</code>
<code> </code><code>/// 这个方法是截图功能的核心方法,也就是绘制截图</code>
<code> </code><code>private</code> <code>void</code> <code>Cutter_MouseMove(</code><code>object</code> <code>sender, MouseEventArgs e)</code>
<code> </code><code>// 确保截图开始</code>
<code> </code><code>if</code> <code>(CatchStart)</code>
<code> </code><code>// 新建一个图片对象,让它与屏幕图片相同</code>
<code> </code><code>Bitmap copyBmp = (Bitmap)originBmp.Clone();</code>
<code> </code><code>// 获取鼠标按下的坐标</code>
<code> </code><code>Point newPoint = </code><code>new</code> <code>Point(DownPoint.X, DownPoint.Y);</code>
<code> </code><code>// 新建画板和画笔</code>
<code> </code><code>Graphics g = Graphics.FromImage(copyBmp);</code>
<code> </code><code>Pen p = </code><code>new</code> <code>Pen(Color.Red, 1);</code>
<code> </code><code>// 获取矩形的长宽</code>
<code> </code><code>int</code> <code>width = Math.Abs(e.X - DownPoint.X);</code>
<code> </code><code>int</code> <code>height = Math.Abs(e.Y-DownPoint.Y);</code>
<code> </code><code>if</code> <code>(e.X < DownPoint.X)</code>
<code> </code><code>{</code>
<code> </code><code>newPoint.X = e.X;</code>
<code> </code><code>if</code> <code>(e.Y < DownPoint.Y)</code>
<code> </code><code>newPoint.Y = e.Y;</code>
<code> </code><code>CatchRectangle = </code><code>new</code> <code>Rectangle(newPoint, </code><code>new</code> <code>Size(width,height));</code>
<code> </code><code>// 将矩形画在画板上</code>
<code> </code><code>g.DrawRectangle(p, CatchRectangle);</code>
<code> </code><code>// 释放目前的画板</code>
<code> </code><code>g.Dispose();</code>
<code> </code><code>p.Dispose();</code>
<code> </code><code>// 从当前窗体创建新的画板</code>
<code> </code><code>Graphics g1 = </code><code>this</code><code>.CreateGraphics();</code>
<code> </code>
<code> </code><code>// 将刚才所画的图片画到截图窗体上</code>
<code> </code><code>// 为什么不直接在当前窗体画图呢?</code>
<code> </code><code>// 如果自己解决将矩形画在窗体上,会造成图片抖动并且有无数个矩形</code>
<code> </code><code>// 这样实现也属于二次缓冲技术</code>
<code> </code><code>g1.DrawImage(copyBmp, </code><code>new</code> <code>Point(0, 0));</code>
<code> </code><code>g1.Dispose();</code>
<code> </code><code>// 释放拷贝图片,防止内存被大量消耗</code>
<code> </code><code>copyBmp.Dispose();</code>
<code> </code><code>/// 鼠标左键弹起事件</code>
<code> </code><code>private</code> <code>void</code> <code>Cutter_MouseUp(</code><code>object</code> <code>sender, MouseEventArgs e)</code>
<code> </code><code>if</code> <code>(e.Button == MouseButtons.Left)</code>
<code> </code><code>// 如果截图已经开始,鼠标左键弹起设置截图完成</code>
<code> </code><code>if</code> <code>(CatchStart)</code>
<code> </code><code>CatchStart = </code><code>false</code><code>;</code>
<code> </code><code>CatchFinished = </code><code>true</code><code>;</code>
<code> </code><code>/// 鼠标双击事件,如果鼠标位于矩形内,则将矩形内的图片保存到剪切板中</code>
<code> </code><code>private</code> <code>void</code> <code>Cutter_MouseDoubleClick(</code><code>object</code> <code>sender, MouseEventArgs e)</code>
<code> </code><code>if</code> <code>(e.Button == MouseButtons.Left && CatchFinished)</code>
<code> </code><code>// 新建一个与矩形一样大小的空白图片</code>
<code> </code><code>Bitmap CatchedBmp = </code><code>new</code> <code>Bitmap(CatchRectangle.Width, CatchRectangle.Height);</code>
<code> </code><code>Graphics g = Graphics.FromImage(CatchedBmp);</code>
<code> </code><code>// 把originBmp中指定部分按照指定大小画到空白图片上</code>
<code> </code><code>// CatchRectangle指定originBmp中指定部分</code>
<code> </code><code>// 第二个参数指定绘制到空白图片的位置和大小</code>
<code> </code><code>// 画完后CatchedBmp不再是空白图片了,而是具有与截取的图片一样的内容</code>
<code> </code><code>g.DrawImage(originBmp, </code><code>new</code> <code>Rectangle(0, 0, CatchRectangle.Width, CatchRectangle.Height), CatchRectangle, GraphicsUnit.Pixel);</code>
<code> </code><code>// 将图片保存到剪切板中</code>
<code> </code><code>Clipboard.SetImage(CatchedBmp);</code>
<code> </code><code>CatchFinished = </code><code>false</code><code>;</code>
<code> </code><code>this</code><code>.BackgroundImage = originBmp;</code>
<code> </code><code>CatchedBmp.Dispose();</code>
5 到第4点为止,截图的功能已经分析完了,之后就是当我们使用QQ截图的时候,我们除了可以点击聊天窗口中的截图按钮来进行截图外,还可以通过按下Alt+Ctrl+A来进行截图,要实现这个功能的思路也很简单——即当聊天窗体加载的时候对热键(程序中我定义的热键是“Alt+Ctrl+C”)进行注册(此时调用了Win32中RegisterHotKey方法来完成热键的注册),当聊天窗体关闭时进行对热键的卸载,防止对热键进行多次注册,此时调用Win32中的UnregisterHotKey方法来完成,具体的实现代码为:
<code> </code><code>/// 窗体加载事件处理</code>
<code> </code><code>/// 在窗体加载时注册热键</code>
<code> </code><code>private</code> <code>void</code> <code>聊天窗体_Load(</code><code>object</code> <code>sender, EventArgs e)</code>
<code> </code><code>uint</code> <code>ctrlHotKey = (</code><code>uint</code><code>)(KeyModifiers.Alt|KeyModifiers.Ctrl);</code>
<code> </code><code>// 注册热键为Alt+Ctrl+C, "100"为唯一标识热键</code>
<code> </code><code>HotKey.RegisterHotKey(Handle, 100, ctrlHotKey, Keys.C);</code>
<code> </code><code>/// 窗体关闭时处理程序</code>
<code> </code><code>/// 窗体关闭时取消热键注册</code>
<code> </code><code>private</code> <code>void</code> <code>聊天窗体_FormClosing(</code><code>object</code> <code>sender, FormClosingEventArgs e)</code>
<code> </code><code>// 卸载热键</code>
<code> </code><code>HotKey.UnregisterHotKey(Handle, 100);</code>
<code> </code><code>#endregion</code>
<code> </code><code>// 热键按下执行的方法</code>
<code> </code><code>private</code> <code>void</code> <code>GlobalKeyProcess()</code>
<code> </code><code>this</code><code>.WindowState = FormWindowState.Minimized;</code>
<code> </code><code>// 窗口最小化也需要一定时间</code>
<code> </code><code>Thread.Sleep(200);</code>
<code> </code><code>btnCutter.PerformClick();</code>
<code> </code><code>/// 重写WndProc()方法,通过监视系统消息,来调用过程</code>
<code> </code><code>/// 监视Windows消息</code>
<code> </code><code>/// <param name="m"></param></code>
<code> </code><code>protected</code> <code>override</code> <code>void</code> <code>WndProc(</code><code>ref</code> <code>Message m)</code>
<code> </code><code>//如果m.Msg的值为0x0312那么表示用户按下了热键</code>
<code> </code><code>const</code> <code>int</code> <code>WM_HOTKEY = 0x0312;</code>
<code> </code><code>switch</code> <code>(m.Msg)</code>
<code> </code><code>case</code> <code>WM_HOTKEY:</code>
<code> </code><code>if</code> <code>(m.WParam.ToString() == </code><code>"100"</code><code>)</code>
<code> </code><code>{</code>
<code> </code><code>GlobalKeyProcess();</code>
<code> </code><code>}</code>
<code> </code><code>break</code><code>;</code>
<code> </code><code>// 将系统消息传递自父类的WndProc</code>
<code> </code><code>base</code><code>.WndProc(</code><code>ref</code> <code>m);</code>
上面已经介绍了实现QQ截图的一个思路的,朋友们是不是迫不及待想看看该程序的一个效果了?下面就通过一个动画来让大家更形象地看到程序的运行效果的:
到这里QQ截图的介绍部分就到这里了,本工具的实现自认为讲解的非常通俗易懂的,希望大家可以这样觉得并且可以更清晰地明白QQ截图的实现思路的,下面附上本专题的所有源码和一个高仿QQ截图的文章:
本文转自LearningHard 51CTO博客,原文链接:http://blog.51cto.com/learninghard/1241405,如需转载请自行联系原作者