1. 程式人生 > >C#:小專案-截圖工具

C#:小專案-截圖工具

1.起因

一直用的截圖是qq的截圖,所以想要實現一個簡單點的截圖,為了方便。

2.思路

講一下實現流程。

  1. 主窗體,上有截圖按鈕,點選進入截圖窗體
  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();
            }
        }
    }
}

完!