一、開發環境及工具 Windows 7 系統,開發軟體為Microsoft Visual Studio Ultimate 2012 二、實現的功能 屏幕截屏,保存到圖片或者保存到剪切板。截屏範圍可以隨意移動或者改變大小,自由度很高。先預覽一下效果: 三、實現原理 共2個窗體,1個是主窗體,主要功能進 ...
一、開發環境及工具
Windows 7 系統,開發軟體為Microsoft Visual Studio Ultimate 2012
二、實現的功能
屏幕截屏,保存到圖片或者保存到剪切板。截屏範圍可以隨意移動或者改變大小,自由度很高。先預覽一下效果:
三、實現原理
共2個窗體,1個是主窗體,主要功能進行熱鍵設置,抓取屏幕到圖片傳給另一個窗體,另一個窗體對傳過來的圖片繼續截取等操作。
四、開發手記
1.新建winform項目
2.主界面設計如圖:
窗體主要屬性設置如下:
//視窗樣式 this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; //視窗圖標 this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); //禁止最大化 this.MaximizeBox = false; //視窗名稱 this.Name = "PicCut"; //視窗初始位置為屏幕中心 this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; //視窗標題為“截圖設置” this.Text = "截圖設置"; //視窗浮在所有視窗上面 this.TopMost = true;
此視窗內主要包括2個label控制項,1個ComboBox和1個PictureBox,擺放位置如上圖。label用於顯示文字說明;ComboBox用於顯示熱鍵0—9個數字供選擇;PictureBox用於顯示二維碼圖片。
3.主界面的主要功能是對熱鍵進行設置,同時我們程式運行,該界面自動最小化到托盤,雙擊進行截圖,有機顯示上述主界面進行設置,設置完畢可以最小化自動到托盤,點擊關閉程式會自動退出。
(1)自動到托盤
主界面拖入“notifyIcon”,主要屬性設置如下圖,其中Text是滑鼠滑動到拖盤區域圖標上的提示;Icon是托盤圖標;Visible設置為false,預設不顯示托盤區域圖標。
主要程式代碼。窗體載入時,最小化窗體,同時設置ComboBox空間的預設值。
private void PicCut_Load(object sender, EventArgs e) {
this.WindowState = FormWindowState.Minimized; NumberCbox.SelectedIndex = CutPicSet.Default.KeyNumber; }
視窗大小發生變化時,如果視窗最小化,隱藏當前窗體,托盤區域顯示圖標。
private void PicCut_SizeChanged(object sender, EventArgs e) { if (this.WindowState == FormWindowState.Minimized) { this.Hide(); MinNot.Visible = true; } }
上述兩段代碼實現了,程式開始運行時候,視窗最小化隱藏,同時托盤區域顯示圖標。
(2)托盤圖標左鍵雙擊進行截圖操作
private void MinNot_MouseDoubleClick(object sender, MouseEventArgs e) { if (e.Button==MouseButtons.Left) {
//顯示截圖窗體
ShowCutPic(); } }
ShowCutPic函數:
protected void ShowCutPic() { // 創建的空白圖片和屏幕大小一樣大的圖片 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 CutPic(); // 截圖窗體的圖片屬性設置我們剛纔新建的圖片; cutter.Image = CatchBmp; //顯示窗體 cutter.ShowDialog(); }
(3)托盤圖標右鍵單擊顯示界面設置
private void MinNot_MouseClick(object sender, MouseEventArgs e) { if (e.Button== MouseButtons.Right) { //顯示主窗體 this.Show(); //隱藏托盤圖標 MinNot.Visible = false; //當前視窗普通設置 this.WindowState = FormWindowState.Normal; } }
(4)熱鍵設置
/// <summary> /// 如果函數執行成功,返回值不為0。 /// 如果函數執行失敗,返回值為0。要得到擴展錯誤信息,調用GetLastError。 /// </summary> /// <param name="hWnd">要定義熱鍵的視窗的句柄</param> /// <param name="id">定義熱鍵ID(不能與其它ID重覆)</param> /// <param name="fsModifiers">標識熱鍵是否在按Alt、Ctrl、Shift、Windows等鍵時才會生效</param> /// <param name="vk">定義熱鍵的內容</param> /// <returns></returns> [DllImport("user32.dll", SetLastError = true)] public static extern bool RegisterHotKey(IntPtr hWnd, int id, KeyModifiers fsModifiers, Keys vk); /// <summary> /// 註銷熱鍵 /// </summary> /// <param name="hWnd">要取消熱鍵的視窗的句柄</param> /// <param name="id">要取消熱鍵的ID</param> /// <returns></returns> [DllImport("user32.dll", SetLastError = true)] public static extern bool UnregisterHotKey(IntPtr hWnd, int id); /// <summary> /// 輔助鍵名稱。 /// Alt, Ctrl, Shift, WindowsKey /// </summary> [Flags()] public enum KeyModifiers { None = 0, Alt = 1, Ctrl = 2, Shift = 4, WindowsKey = 8 } /// <summary> /// 註冊熱鍵 /// </summary> /// <param name="hwnd">視窗句柄</param> /// <param name="hotKey_id">熱鍵ID</param> /// <param name="keyModifiers">組合鍵</param> /// <param name="key">熱鍵</param> public static void RegHotKey(IntPtr hwnd, int hotKeyId, KeyModifiers keyModifiers, Keys key) { if (!RegisterHotKey(hwnd, hotKeyId, keyModifiers, key)) { int errorCode = Marshal.GetLastWin32Error(); if (errorCode == 1409) { MessageBox.Show("熱鍵被占用 !"); } else { MessageBox.Show("註冊熱鍵失敗!錯誤代碼:" + errorCode); } } } /// <summary> /// 註銷熱鍵 /// </summary> /// <param name="hwnd">視窗句柄</param> /// <param name="hotKey_id">熱鍵ID</param> public static void UnRegHotKey(IntPtr hwnd, int hotKeyId) { //註銷指定的熱鍵 UnregisterHotKey(hwnd, hotKeyId); }
private const int WM_HOTKEY = 0x312; //視窗消息-熱鍵 private const int WM_CREATE = 0x1; //視窗消息-創建 private const int WM_DESTROY = 0x2; //視窗消息-銷毀 private const int Space = 0x3572; //熱鍵ID protected override void WndProc(ref Message m) { base.WndProc(ref m); switch (m.Msg) { case WM_HOTKEY: //視窗消息-熱鍵ID switch (m.WParam.ToInt32()) { case Space: //熱鍵ID //按下熱鍵顯示截圖窗體 ShowCutPic(); break; default: break; } break; case WM_CREATE: //視窗消息-創建 SetHotKey(CutPicSet.Default.KeyNumber); break; case WM_DESTROY: //視窗消息-銷毀 UnRegHotKey(Handle, Space); //銷毀熱鍵 break; default: break; } } public void SetHotKey(int keynumber) { if (keynumber == 0) { RegHotKey(Handle, Space, KeyModifiers.Ctrl, Keys.D0); } else if (keynumber == 1) { RegHotKey(Handle, Space, KeyModifiers.Ctrl, Keys.D1); } else if (keynumber == 2) { RegHotKey(Handle, Space, KeyModifiers.Ctrl, Keys.D2); } else if (keynumber == 3) { RegHotKey(Handle, Space, KeyModifiers.Ctrl, Keys.D3); } else if (keynumber == 4) { RegHotKey(Handle, Space, KeyModifiers.Ctrl, Keys.D4); } else if (keynumber == 5) { RegHotKey(Handle, Space, KeyModifiers.Ctrl, Keys.D5); } else if (keynumber == 6) { RegHotKey(Handle, Space, KeyModifiers.Ctrl, Keys.D6); } else if (keynumber == 7) { RegHotKey(Handle, Space, KeyModifiers.Ctrl, Keys.D7); } else if (keynumber == 8) { RegHotKey(Handle, Space, KeyModifiers.Ctrl, Keys.D8); } else { RegHotKey(Handle, Space, KeyModifiers.Ctrl, Keys.D9); } }
(5)圖形熱鍵設置
private void NumberCbox_SelectedIndexChanged(object sender, EventArgs e) { //把配置文件設置NumberCbox的選擇值 CutPicSet.Default.KeyNumber = NumberCbox.SelectedIndex; CutPicSet.Default.Save(); //先卸載原來註冊的熱鍵 UnRegHotKey(Handle, Space); //從新設置選擇的熱鍵 SetHotKey(NumberCbox.SelectedIndex); }
4、截圖界面設計
新建窗體CutPic,名稱與之前的名稱相對應,主要屬性設置如下:
Name:CutPic
WindowState:Maximized
TopMost:true
FormBorderStyle:None
5、截圖功能實現
主要代碼如下:
private Point m_ptStart; //起始點位置 private Point m_ptCurrent; //當前滑鼠位置 private Point m_ptTempForMove; //移動選框的時候臨時用 private Rectangle m_rectClip; //限定滑鼠活動的區域 private Rectangle[] m_rectDots = new Rectangle[8]; //八個控制點 protected bool m_bMoving; protected bool m_bChangeWidth; protected bool m_bChangeHeight; protected bool m_bMouseHover; private bool _IsDrawed; /// 獲取當前是否已經選擇區域 private Image _Image; /// /// 要裁剪的圖像 /// [Description("要裁剪的圖像"), Category("Customs")] public Image Image { get { return _Image; } set { if (value == this._Image) return; _Image = value; // this.Clear(); } } private Color _MaskColor = Color.FromArgb(125, 0, 0, 0); /// /// 遮罩顏色 /// [Description("遮罩顏色"), Category("Customs")] public Color MaskColor { get { return _MaskColor; } set { if (_MaskColor == value) return; _MaskColor = value; if (this._Image != null) this.Invalidate(); } } private Rectangle _SelectedRectangle;/// 獲取或設置懸著區域 public CutPic() { SetStyle(ControlStyles.UserPaint, true); SetStyle(ControlStyles.AllPaintingInWmPaint, true); // 禁止擦除背景. SetStyle(ControlStyles.DoubleBuffer, true); // 雙緩衝 InitializeComponent(); } private void CutPic_MouseDown(object sender, MouseEventArgs e) { if (this._Image == null) { return;//Image屬性null或者已經鎖定選擇 直接返回 } m_ptStart = e.Location; m_bChangeHeight = true; m_bChangeWidth = true; //判斷若不在限定範圍內操作 返回 m_rectClip = this.DisplayRectangle; Size sz = this.Size; sz = this.Size; m_rectClip.Intersect(new Rectangle(Point.Empty, sz)); m_rectClip.Width++; m_rectClip.Height++; Cursor.Clip = RectangleToScreen(m_rectClip); if (ToolsPanel.Visible==true) { ToolsPanel.Visible = false; } //如果 已經選擇區域 若滑鼠點下 判斷是否在控制頂點上 if (this._IsDrawed) { this._IsDrawed = false; //預設表示 要更改選取設置 清楚IsDrawed屬性 if (m_rectDots[0].Contains(e.Location)) { m_ptStart.X = this._SelectedRectangle.Right; m_ptStart.Y = this._SelectedRectangle.Bottom; } else if (m_rectDots[1].Contains(e.Location)) { m_ptStart.Y = this._SelectedRectangle.Bottom; m_bChangeWidth = false; } else if (m_rectDots[2].Contains(e.Location)) { m_ptStart.X = this._SelectedRectangle.X; m_ptStart.Y = this._SelectedRectangle.Bottom; } else if (m_rectDots[3].Contains(e.Location)) { m_ptStart.X = this._SelectedRectangle.Right; m_bChangeHeight = false; } else if (m_rectDots[4].Contains(e.Location)) { m_ptStart.X = this._SelectedRectangle.X; m_bChangeHeight = false; } else if (m_rectDots[5].Contains(e.Location)) { m_ptStart.X = this._SelectedRectangle.Right; m_ptStart.Y = this._SelectedRectangle.Y; } else if (m_rectDots[6].Contains(e.Location)) { m_ptStart.Y = this._SelectedRectangle.Y; m_bChangeWidth = false; } else if (m_rectDots[7].Contains(e.Location)) { m_ptStart = this._SelectedRectangle.Location; } else if (this._SelectedRectangle.Contains(e.Location)) { m_bMoving = true; m_bChangeWidth = false; m_bChangeHeight = false; } else { this._IsDrawed = true; } //若以上條件不成立 表示不需要更改設置 } } private void CutPic_MouseClick(object sender, MouseEventArgs e) { if (MouseButtons.Right==e.Button) { this.DialogResult = DialogResult.OK; this.Close(); } } private void CutPic_Paint(object sender, PaintEventArgs e) { Graphics g = e.Graphics; if (_Image!=null) { g.DrawImage(this._Image,0,0,this._Image.Width,this._Image.Height);//原圖 using (SolidBrush sb = new SolidBrush(this._MaskColor)) { g.FillRectangle(sb, this.ClientRectangle);//遮罩 } if (!this._SelectedRectangle.IsEmpty) this.DrawSelectedRectangle(g);//選框 } SetPanleLocation(); } private void CutPic_Load(object sender, EventArgs e) { for (int i = 0; i < 8; i++) { m_rectDots[i].Size = new Size(5, 5); } m_ptTempForMove = this.DisplayRectangle.Location; } private void CutPic_MouseMove(object sender, MouseEventArgs e) { m_ptCurrent = e.Location; if (this._Image == null) { return; } if (this._IsDrawed) {//如果已經繪製 移動過程中判斷是否需要設置滑鼠樣式 this.SetCursorStyle(e.Location); } else if (e.Button == MouseButtons.Left) {//否則可能表示在選擇區域或重置大小 if (m_bChangeWidth) {//是否允許選區寬度改變 如重置大小時候 拉動上邊和下邊中點時候 this._SelectedRectangle.X = e.Location.X > m_ptStart.X ? m_ptStart.X : e.Location.X; this._SelectedRectangle.Width = Math.Abs(e.Location.X - m_ptStart.X); } if (m_bChangeHeight) { this._SelectedRectangle.Y = e.Location.Y > m_ptStart.Y ? m_ptStart.Y : e.Location.Y; this._SelectedRectangle.Height = Math.Abs(e.Location.Y - m_ptStart.Y); } if (m_bMoving) {//如果是移動選區 判斷選區移動範圍 int tempX = m_ptTempForMove.X + e.X - m_ptStart.X; int tempY = m_ptTempForMove.Y + e.Y - m_ptStart.Y; if (tempX < 0) tempX = 0; if (tempY < 0) tempY = 0; if (this._SelectedRectangle.Width + tempX >= m_rectClip.Width) tempX = m_rectClip.Width - this._SelectedRectangle.Width - 1; if (this._SelectedRectangle.Height + tempY >= m_rectClip.Height) tempY = m_rectClip.Height - this._SelectedRectangle.Height - 1; this._SelectedRectangle.X = tempX; this._SelectedRectangle.Y = tempY; } this.Invalidate(); } else if (!this._IsDrawed) { this.Invalidate();//否則 在需要繪製放大鏡並且還沒有選好區域同時 都重繪 } } /// /// 判斷滑鼠當前位置顯示樣式 /// /// 滑鼠坐標 protected virtual void SetCursorStyle(Point pt) { if (m_rectDots[0].Contains(pt) || m_rectDots[7].Contains(pt)) this.Cursor = Cursors.SizeNWSE; else if (m_rectDots[1].Contains(pt) || m_rectDots[6].Contains(pt)) this.Cursor = Cursors.SizeNS; else if (m_rectDots[2].Contains(pt) || m_rectDots[5].Contains(pt)) this.Cursor = Cursors.SizeNESW; else if (m_rectDots[3].Contains(pt) || m_rectDots[4].Contains(pt)) this.Cursor = Cursors.SizeWE; else if (this._SelectedRectangle.Contains(pt)) this.Cursor = Cursors.SizeAll; else this.Cursor = Cursors.Default; } private void CutPic_MouseUp(object sender, MouseEventArgs e) { this._IsDrawed = !this._SelectedRectangle.IsEmpty; m_ptTempForMove = this._SelectedRectangle.Location; m_bMoving = false; m_bChangeWidth = false; m_bChangeHeight = false; Cursor.Clip = Rectangle.Empty; ToolsPanel.Visible = true; this.Invalidate(); } public void SetPanleLocation() { ToolsPanel.Left = this._SelectedRectangle.Left + this._SelectedRectangle.Width - ToolsPanel.Width; ToolsPanel.Top = this._SelectedRectangle.Top+this._SelectedRectangle.Height+5; } /// /// 繪製選框 /// /// 繪圖錶面 protected virtual void DrawSelectedRectangle(Graphics g) { m_rectDots[0].Y = m_rectDots[1].Y = m_rectDots[2].Y = this._SelectedRectangle.Y - 2; m_rectDots[5].Y = m_rectDots[6].Y = m_rectDots[7].Y = this._SelectedRectangle.Bottom - 2; m_rectDots[0].X = m_rectDots[3].X = m_rectDots[5].X = this._SelectedRectangle.X - 2; m_rectDots[2].X = m_rectDots[4].X = m_rectDots[7].X = this._SelectedRectangle.Right - 2; m_rectDots[3].Y = m_rectDots[4].Y = this._SelectedRectangle.Y + this._SelectedRectangle.Height / 2 - 2; m_rectDots[1].X = m_rectDots[6].X = this._SelectedRectangle.X + this._SelectedRectangle.Width / 2 - 2; g.DrawImage(this._Image, this._SelectedRectangle, this._SelectedRectangle, GraphicsUnit.Pixel); g.DrawRectangle(Pens.Cyan, this._SelectedRectangle.Left, this._SelectedRectangle.Top, this._SelectedRectangle.Width - 1, this._SelectedRectangle.Height - 1); foreach (Rectangle rect in m_rectDots) g.FillRectangle(Brushes.Yellow, rect); string str = string.Format("X:{0} Y:{1} W:{2} H:{3}", this._SelectedRectangle.Left, this._SelectedRectangle.Top, this._SelectedRectangle.Width, this._SelectedRectangle.Height); Size szStr = g.MeasureString(str, this.Font).ToSize(); Point ptStr = new Point(this._SelectedRectangle.Left, this._SelectedRectangle.Top - szStr.Height - 5); if (ptStr.Y < 0) ptStr.Y = this._SelectedRectangle.Top + 5; if (ptStr.X + szStr.Width > this.Width) ptStr.X = this.Width - szStr.Width; using (SolidBrush sb = new SolidBrush(Color.FromArgb(125, 0, 0, 0))) { g.FillRectangle(sb, new Rectangle(ptStr, szStr)); g.DrawString(str, this.Font, Brushes.White, ptStr); } } private void SaveBT_Click(object sender, EventArgs e) { SaveFileDialog saveFileDialog = new SaveFileDialog(); saveFileDialog.FileName = DateTime.Now.ToString("yyyyMMddhhmmss"); saveFileDialog.Filter = "png|*.png|bmp|*.bmp|jpg|*.jpg|gif|*.gif"; if (saveFileDialog.ShowDialog() != DialogResult.Cancel) { System.Drawing.Rectangle cropArea = new System.Drawing.Rectangle(_SelectedRectangle.X, _SelectedRectangle.Y, _SelectedRectangle.Width, _SelectedRectangle.Height); Bitmap bmpImage = new Bitmap(this._Image); Bitmap bmpCrop = bmpImage.Clone(cropArea, bmpImage.PixelFormat); bmpCrop.Save(saveFileDialog.FileName); this.DialogResult = DialogResult.OK; this.Close(); } else { this.Focus(); } } private void CutPic_MouseDoubleClick(object sender, MouseEventArgs e) { if (this._SelectedRectangle.Contains(e.Location)) { //複製圖片到剪切板 System.Drawing.Rectangle cropArea = new System.Drawing.Rectangle(_SelectedRectangle.X, _SelectedRectangle.Y, _SelectedRectangle.Width, _SelectedRectangle.Height); Bitmap bmpImage = new Bitmap(this._Image); Bitmap bmpCrop = bmpImage.Clone(cropArea, bmpImage.PixelFormat); Clipboard.SetImage(bmpCrop); this.DialogResult = DialogResult.OK; this.Close(); } } //如果按下ESC鍵退出截圖功能 private void CutPic_KeyUp(object sender, KeyEventArgs e) { if (e.KeyValue==27) { this.DialogResult = DialogResult.OK; this.Close(); } }
這樣截圖功能就已經實現了。源代碼下載,請點擊