C#:小專案-截圖工具
阿新 • • 發佈:2018-12-27
1.起因
一直用的截圖是qq的截圖,所以想要實現一個簡單點的截圖,為了方便。
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(); } } } }
完!