[C# 開發技巧]實現屬於本身的截圖工具

1、引言

以前一直都是寫一些C#基礎知識的內容的,然而有些初學者可能看完了這些基礎知識以後,會有這樣一個疑惑的——我瞭解了這些基礎知識以後,我想作一些工具怎麼仍是不會作的呢?那些基礎知識到底有什麼用的了?然而我剛開始寫這個系列的初衷主要是我想系統地去研究下C#各個階段的特性的,及時有些特性我知道它是怎麼用的,可是每次遇到問題的時候確實百度能夠能夠解決不少問題,可是本身老是以爲有點「虛」,然而經過寫完這個系列以後,我不少知識點均可以串起來了,能夠作到一個觸類旁通的一個效果的,當我遇到實際問題的也不可能徹底本身寫出來,一樣也會百度找解決方案,可是此時我卻沒有 「虛」的感受,由於我知道這個東西,而且我也知道如何正確的百度這個問題。因此對於基礎知識的學習仍是頗有必要的,由於系統學完以後,你能夠更好地找到你遇到問題的答案,由於我有時候會看到一些朋友在QQ羣中提到,遇到某個問題都不知道百度什麼的,然而系統地學習基礎徹底能夠幫助你快速地百度,(其實找答案也是一種能力),然而對於第一個疑惑的解答就是——系統學習完,確實剛開始的確開發工具不會作,可是實際寫代碼是很簡單,而且如今大部分應用你百度下均可以找到的,因此代碼並非問題,主要是解決問題的思路,而且實際工具的開發也是對一個基礎知識的鞏固,從而對問題達到一個觸類旁通的效果。html

上面說了這麼多的(可能說的有點多),主要是讓你們明白,系統學習C#基礎知識是頗有必要的,系統學習完C#基礎知識以後就是代碼量的積累了,也就是本身作一些小工具,積累到必定代碼量以後,就能夠嘗試寫寫一些大的項目或開源項目等,因此在後面的系列中將會分享一些具體工具的開發,同時這也是我本身的一個學習的計劃,這裏分享給你們但願對一些迷茫的朋友有所幫助。若是你如今尚未明確或更好地目標,而且也是從事.NET工做或學習的朋友,那就和我一塊兒靜下心來學編程,下面是個人一個學習方向圖(可能多少有點誤差,相信大體意圖你們能夠明白):編程

2、實現思路

囉嗦了這麼多,下面就具體介紹下實現截圖工具的實現思路。c#

爲了讓你們更清楚地知道如何去實現本身的截圖工具,首先我來描述下截圖的一個過程——咱們使用QQ的截圖工具和Windows 自帶的截圖工具均可以發現,當咱們點擊QQ窗體中的截圖按鈕時,此時咱們將看到一個全屏圖片,而後咱們能夠在其上截圖,當鼠標左鍵按下時,即表明開始截圖,並咱們能夠移動鼠標來改變截圖的大小,鼠標彈起時即表明結束截圖,此時咱們能夠雙擊矩形區域徹底截圖,而且能夠經過粘貼操做把截取的圖片粘貼到聊天窗口的發送區,鼠標右鍵點擊則是退出截圖。這樣咱們截圖的過程描述就是這樣的,從這個描述中咱們就能夠抽象出實現咱們截圖工具的思路來:windows

  1. 從 「此時咱們將看到一個全屏圖片」這句話描述咱們應該抽象爲——對於QQ截圖工具的實現來講,咱們看到的這個全屏圖片其實並非一張「圖片」(這裏最好不要鑽空子),而是一個窗體,這個窗體咱們命名爲 「截圖窗體」,只是把窗體的背景圖片設置爲全屏圖片。說到這裏,一些沒有研究過QQ截圖工具的人開始有疑問了——咱們看到的是窗體?那爲何邊框的,即沒有最大化按鈕,最下化按鈕的呢?(對於這點的解釋就是,程序中能夠設置Form的BorderStyle屬性爲none的方式來隱藏掉邊框)。ide

  2. 既然要設置窗體的背景圖片爲全屏圖片,咱們知道設置背景圖片只須要設置窗體的BackgroundImage屬性就行了,可是全屏圖片怎麼獲取呢?既然是全屏圖片,天然我就應該使窗體最大化話了,否則咱們看到只是一個沒有邊框的「小圖片」了,而不是一個全屏的圖片。下面是具體實現這個分析的代碼:函數

// 經過Graphics的CopyFromScreen方法把全屏圖片的拷貝到咱們定義好的一個和屏幕大小相同的空白圖片中,
            // 拷貝完成以後,CatchBmp就是全屏圖片的拷貝了,而後指定爲截圖窗體背景圖片就行了。
            // 新建一個和屏幕大小相同的圖片
            Bitmap CatchBmp = new Bitmap(Screen.AllScreens[0].Bounds.Width, Screen.AllScreens[0].Bounds.Height);
                         
            // 建立一個畫板,讓咱們能夠在畫板上畫圖
            // 這個畫板也就是和屏幕大小同樣大的圖片
            // 咱們能夠經過Graphics這個類在這個空白圖片上畫圖
            Graphics g = Graphics.FromImage(CatchBmp);
            // 把屏幕圖片拷貝到咱們建立的空白圖片 CatchBmp中
            g.CopyFromScreen(new Point(0, 0), new Point(0, 0), new Size(Screen.AllScreens[0].Bounds.Width, Screen.AllScreens[0].Bounds.Height));
            // 建立截圖窗體
            cutter = new Cutter();
            // 指示窗體的背景圖片爲屏幕圖片
            cutter.BackgroundImage = CatchBmp;

3. 從 「而後咱們能夠在其上截圖」這句話中咱們抽象爲——其實咱們截圖操做,從程序角度來講就是咱們在這個最大化的窗體中畫圖,可能這個對一些不瞭解GDI+畫圖的朋友有些難理解,這裏作個比喻——咱們會拿筆在紙上畫圖,咱們能夠用比畫三角形,矩形已經各類圖形,此時紙就是咱們一個畫板,筆是用來畫圖圖形的,同時筆也是有顏色和粗細的,咱們能夠用紅色水筆畫,畫出來的圖就是紅色的了,也能夠用黑色水筆畫,天然畫出來的就是黑色的了,一樣,在GDI+也就是Graphics Device Interface Plus也就是圖形設備接口,在.NET 中也提供了一些這樣的類來讓咱們實現對圖像的訪問,也就是咱們可使用.NET中提供的類來進行 「畫畫」要畫畫固然必需要有畫板吧(咱們開始比喻中紙就是畫板),在.NET 類中Graphics類就是對畫板的抽象,畫板能夠由三種方式建立:(1)從圖片或繼承自圖像對象中建立;(2)從窗體或控件的Paint事件中建立;(3)利用窗體或控件的CreateGraphics方法建立。有了畫板以後,固然就須要筆來畫畫了,在.NET 中Pen類就是起到筆的做用,在構造函數中能夠指定筆的顏色和粗細,有了筆以後就是開始畫圖了,在.NET中也一樣提供了一些方法來完成畫圖,如DrawRectangle方法——畫矩形工具

  4. 從 「當鼠標左鍵按下時,即表明開始截圖,並咱們能夠移動鼠標來改變截圖的大小,鼠標彈起時即表明結束截圖,此時咱們能夠雙擊矩形區域徹底截圖,而且能夠經過粘貼操做把截取的圖片粘貼到聊天窗口的發送區,鼠標右鍵點擊則是退出截圖」這些描述中能夠抽象爲——鼠標的移動,按下,彈起等操做,在程序角度來講,也就是實現截圖窗體的MouseMove事件(對應於鼠標移動),MouseDown事件(對應於鼠標左鍵按下),MouseClick事件(對應於鼠標右鍵結束截圖)、MouseUp(對應於鼠標彈起結束截圖)和MouseDoubleClick(鼠標雙擊矩形區域徹底截圖,並能夠經過粘貼操做把截取的圖片粘貼到聊天窗口的發送區,既然能夠進行粘貼操做來得到截取圖片,因此必須在該事件中對剪切板設置截圖圖片),3和4的分析過程也是截圖功能的核心實現,對應於下面的代碼(代碼中有詳細解釋,而且你們理解的時候能夠結合3和4的分析):學習

/// <summary>
        /// 鼠標右鍵點擊結束截圖
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Cutter_MouseClick(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Right)
            {
                this.DialogResult = DialogResult.OK;
                this.Close();
            }
        }
        /// <summary>
        /// 鼠標按下事件處理程序
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Cutter_MouseDown(object sender, MouseEventArgs e)
        {
            // 鼠標左鍵按下是開始畫圖,也就是截圖
            if (e.Button == MouseButtons.Left)
            {
                // 若是捕捉沒有開始
                if (!CatchStart)
                {             
                    CatchStart = true;
                    // 保存此時鼠標按下座標
                    DownPoint = new Point(e.X, e.Y);
                }
            }
        }
        /// <summary>
        /// 鼠標移動事件處理程序,即用戶改變截圖大小的處理
        ///  這個方法是截圖功能的核心方法,也就是繪製截圖
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Cutter_MouseMove(object sender, MouseEventArgs e)
        {
            // 確保截圖開始
            if (CatchStart)
            {
                // 新建一個圖片對象,讓它與屏幕圖片相同
                Bitmap copyBmp = (Bitmap)originBmp.Clone();
                // 獲取鼠標按下的座標
                Point newPoint = new Point(DownPoint.X, DownPoint.Y);
                // 新建畫板和畫筆
                Graphics g = Graphics.FromImage(copyBmp);
                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)
                {
                    newPoint.X = e.X;
                }
                if (e.Y < DownPoint.Y)
                {
                    newPoint.Y = e.Y;
                }
                CatchRectangle = new Rectangle(newPoint, new Size(width,height));
                         
                // 將矩形畫在畫板上
                g.DrawRectangle(p, CatchRectangle);
                // 釋放目前的畫板
                g.Dispose();
                p.Dispose();
                // 從當前窗體建立新的畫板
                Graphics g1 = this.CreateGraphics();
                        
                // 將剛纔所畫的圖片畫到截圖窗體上
                // 爲何不直接在當前窗體畫圖呢?
                // 若是本身解決將矩形畫在窗體上,會形成圖片抖動而且有無數個矩形
                // 這樣實現也屬於二次緩衝技術
                g1.DrawImage(copyBmp, new Point(0, 0));
                g1.Dispose();
                // 釋放拷貝圖片,防止內存被大量消耗
                copyBmp.Dispose();
            }
        }
        /// <summary>
        /// 鼠標左鍵彈起事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Cutter_MouseUp(object sender, MouseEventArgs e)
        {
          if (e.Button == MouseButtons.Left)
            {
                // 若是截圖已經開始,鼠標左鍵彈起設置截圖完成
                if (CatchStart)
                {
                    CatchStart = false;
                    CatchFinished = true;
                }
            }
        }
        /// <summary>
        /// 鼠標雙擊事件,若是鼠標位於矩形內,則將矩形內的圖片保存到剪切板中
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Cutter_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left && CatchFinished)
            {
                // 新建一個與矩形同樣大小的空白圖片
                Bitmap CatchedBmp = new Bitmap(CatchRectangle.Width, CatchRectangle.Height);
                Graphics g = Graphics.FromImage(CatchedBmp);
                // 把originBmp中指定部分按照指定大小畫到空白圖片上
                // CatchRectangle指定originBmp中指定部分
                // 第二個參數指定繪製到空白圖片的位置和大小
                // 畫完後CatchedBmp再也不是空白圖片了,而是具備與截取的圖片同樣的內容
                g.DrawImage(originBmp, new Rectangle(0, 0, CatchRectangle.Width, CatchRectangle.Height), CatchRectangle, GraphicsUnit.Pixel);
                // 將圖片保存到剪切板中
                Clipboard.SetImage(CatchedBmp);
                g.Dispose();
                CatchFinished = false;
                this.BackgroundImage = originBmp;
                CatchedBmp.Dispose();
                this.DialogResult = DialogResult.OK;
                this.Close();
            }
        }

 5 到第4點爲止,截圖的功能已經分析完了,以後就是當咱們使用QQ截圖的時候,咱們除了能夠點擊聊天窗口中的截圖按鈕來進行截圖外,還能夠經過按下Alt+Ctrl+A來進行截圖,要實現這個功能的思路也很簡單——即當聊天窗體加載的時候對熱鍵(程序中我定義的熱鍵是「Alt+Ctrl+C」)進行註冊(此時調用了Win32中RegisterHotKey方法來完成熱鍵的註冊),當聊天窗體關閉時進行對熱鍵的卸載,防止對熱鍵進行屢次註冊,此時調用Win32中的UnregisterHotKey方法來完成,具體的實現代碼爲:開發工具

/// <summary>
        /// 窗體加載事件處理
        /// 在窗體加載時註冊熱鍵
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void 聊天窗體_Load(object sender, EventArgs e)
        {
            uint ctrlHotKey = (uint)(KeyModifiers.Alt|KeyModifiers.Ctrl);
            // 註冊熱鍵爲Alt+Ctrl+C, "100"爲惟一標識熱鍵
            HotKey.RegisterHotKey(Handle, 100, ctrlHotKey, Keys.C);
        }
        /// <summary>
        /// 窗體關閉時處理程序
        /// 窗體關閉時取消熱鍵註冊
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void 聊天窗體_FormClosing(object sender, FormClosingEventArgs e)
        {
            // 卸載熱鍵
            HotKey.UnregisterHotKey(Handle, 100);
        }
        #endregion
        // 熱鍵按下執行的方法
        private void GlobalKeyProcess()
        {
            this.WindowState = FormWindowState.Minimized;
            // 窗口最小化也須要必定時間
            Thread.Sleep(200);
            btnCutter.PerformClick();
        }
        /// <summary>
        /// 重寫WndProc()方法,經過監視系統消息,來調用過程
        /// 監視Windows消息
        /// </summary>
        /// <param name="m"></param>
        protected override void WndProc(ref Message m)
        {
            //若是m.Msg的值爲0x0312那麼表示用戶按下了熱鍵
            const int WM_HOTKEY = 0x0312;
            switch (m.Msg)
            {
                case WM_HOTKEY:
                    if (m.WParam.ToString() == "100")
                    {
                        GlobalKeyProcess();
                    }
                    break;
            }
            // 將系統消息傳遞自父類的WndProc
            base.WndProc(ref m);
        }

3、實現效果

上面已經介紹了實現QQ截圖的一個思路的,朋友們是否是火燒眉毛想看看該程序的一個效果了?下面就經過一個動畫來讓你們更形象地看到程序的運行效果的:動畫

4、總結

到這裏QQ截圖的介紹部分就到這裏了,本工具的實現自認爲講解的很是通俗易懂的,但願你們能夠這樣以爲而且能夠更清晰地明白QQ截圖的實現思路的,下面附上本專題的全部源碼和一個高仿QQ截圖的文章:

本專題源碼:http://files.cnblogs.com/zhili/QQ%E6%88%AA%E5%9B%BE%E5%B7%A5%E5%85%B7.zip

高仿騰訊QQ實現:http://blog.csdn.net/crystal_lz/article/details/8274277

相關文章
相關標籤/搜索