天天看點

[C# 開發技巧]實作屬于自己的截圖工具一、引言二、實作思路三、實作效果四、總結

上面說了這麼多的(可能說的有點多),主要是讓大家明白,系統學習C#基礎知識是很有必要的,系統學習完C#基礎知識之後就是代碼量的積累了,也就是自己做一些小工具,積累到一定代碼量之後,就可以嘗試寫寫一些大的項目或開源項目等,是以在後面的系列中将會分享一些具體工具的開發,同時這也是我自己的一個學習的計劃,這裡分享給大家希望對一些迷茫的朋友有所幫助。如果你現在還沒有明确或更好地目标,并且也是從事.NET工作或學習的朋友,那就和我一起靜下心來學程式設計,下面是我的一個學習方向圖(可能多少有點偏差,相信大緻意圖大家可以明白):

[C# 開發技巧]實作屬于自己的截圖工具一、引言二、實作思路三、實作效果四、總結

啰嗦了這麼多,下面就具體介紹下實作截圖工具的實作思路。

為了讓大家更清楚地知道如何去實作自己的截圖工具,首先我來描述下截圖的一個過程——我們使用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>/// &lt;summary&gt;</code>

<code>        </code><code>/// 滑鼠右鍵點選結束截圖</code>

<code>        </code><code>/// &lt;/summary&gt;</code>

<code>        </code><code>/// &lt;param name="sender"&gt;&lt;/param&gt;</code>

<code>        </code><code>/// &lt;param name="e"&gt;&lt;/param&gt;</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>/// &lt;summary&gt;</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 &lt; DownPoint.X)</code>

<code>                </code><code>{</code>

<code>                    </code><code>newPoint.X = e.X;</code>

<code>                </code><code>if</code> <code>(e.Y &lt; 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 &amp;&amp; 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>/// &lt;param name="m"&gt;&lt;/param&gt;</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截圖的一個思路的,朋友們是不是迫不及待想看看該程式的一個效果了?下面就通過一個動畫來讓大家更形象地看到程式的運作效果的:

[C# 開發技巧]實作屬于自己的截圖工具一、引言二、實作思路三、實作效果四、總結

到這裡QQ截圖的介紹部分就到這裡了,本工具的實作自認為講解的非常通俗易懂的,希望大家可以這樣覺得并且可以更清晰地明白QQ截圖的實作思路的,下面附上本專題的所有源碼和一個高仿QQ截圖的文章:

     本文轉自LearningHard 51CTO部落格,原文連結:http://blog.51cto.com/learninghard/1241405,如需轉載請自行聯系原作者