对于QQ截图,肯定是早就有认识了,只是一直没有去认真观察这个操作的具体实现步骤。所以这里将自己的记忆中的步骤简单的写一下:
习惯性用QQ或者TIM的人,一般是使用Ctrl+Alt+A 快捷键(热键)快速实现截图。
- Ctrl+Alt+A 进入截图模式
- 鼠标左键点击
- 鼠标拖动对截图去进行选取
- 鼠标左键弹起
- 双击截图区域 保存图片到剪贴板
- 鼠标右键点击
- 退出截图模式
因为考虑到截图模式的时候 一般只能显示一个窗体 所以就考虑使用单例模式 在ScreenBody窗体中实现以下代码
1:创建单例
private static ScreenBody screenBody=null;
2:私有化构造函数
private ScreenBody() { InitializeComponent(); }
3:创建静态方法
private static ScreenBody GetSingle() { if(screenBody==null) { screenBody=new ScreenBody(); } return screenBody; }
进一步讨论一下在Main窗体中的调用 Main中添加了一个button 命名为btnCutter
private void btnCutter_Click(object sender,EventArgs e) { //新建一个和屏幕大小相同的图片img 也可以用BitMap image img=new Bitmap(Screen.AllScreens[0].Bounds.Width,Screen.AllScreens[0].Bounds.Height); //创建一个画板 让我们可以在画板上画图 大小和屏幕大小一样大 Graphics g=Graphics.FromImage(img); //将屏幕图片拷贝到空白图片img g.CopyFromScreen(new Point(0,0),new Point(0,0),Screen.AllScreens[0].Bounds.Size); //创建截图窗体 ScreenBody body=ScreenBody.GetSingle(); //指示窗体的背景图片为屏幕图片 body.BackGroundImage=img; body.ShowDialog(); }
对于窗体ScreenBody
声明全局变量
private bool CatchStart;//判断鼠标是否按下 private bool CatchFinished;//判断矩形是否绘制完成 private Point DownPoint;//鼠标按下的点 private Image baseMap;//最基本的图片 private Rectangle CatchRectangle;
必须要实现的那几个事件
- 鼠标按下MouseDown
-
private void ScreenBody_MouseDown(object sender, MouseEventArgs e) { //鼠标左键按下就是开始画图,也就是截图 if (e.Button == MouseButtons.Left) { if (CatchStart == false) { CatchStart = true; //保存此时的坐标 DownPoint = new Point(e.X, e.Y); } } }
鼠标移动 MouseMove
-
private void ScreenBody_MouseMove(object sender, MouseEventArgs e) { //确保截图开始 if (CatchStart) { //新建一个图片,让它与屏幕图片相同 Bitmap copyBmp = (Bitmap)baseMap.Clone(); //鼠标按下时的坐标 Point newPoint = new Point(DownPoint.X, DownPoint.Y); //新建画板和画笔 Graphics g = Graphics.FromImage(copyBmp); Pen p = new Pen(Color.Azure, 1);//画笔的颜色为azure 宽度为1 //获取矩形的长度 int width = Math.Abs(e.X - DownPoint.Y); 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(); }
鼠标弹起 Mouseup
-
/// <summary> /// 鼠标左键弹起事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void ScreenBody_MouseUp(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left) { //如果截图已经开始,鼠标左键弹起设置截图完成 if (CatchStart) { CatchStart = false; CatchFinished = true; } } }
鼠标双击
-
private void ScreenBody_MouseDoubleClick(object sender, MouseEventArgs e) { if (e.Button==MouseButtons.Left&&CatchFinished) { //新建一个矩形大小相同的空白图片 Bitmap CatcheBmp = new Bitmap(CatchRectangle.Width, CatchRectangle.Height); Graphics g = Graphics.FromImage(CatcheBmp); ; //把basemap中指定的部分按照指定大小画到空白图片上 //CatchRectangle指定的baseMap中指定的部分 //第二个参数指定绘制到空白图片的位置和大小 //画完后CatchedBmp不再是空白图片,而是具有与截取的图片一样的内容 g.DrawImage(baseMap, new Rectangle(0, 0, CatchRectangle.Width, CatchRectangle.Height)); //将图片保存到剪切板中 Clipboard.SetImage(CatcheBmp); g.Dispose(); CatchFinished = false; this.BackgroundImage = baseMap; CatcheBmp.Dispose(); this.DialogResult = DialogResult.OK; this.Close(); } }
鼠标右键 退出截图
-
/// <summary> /// 鼠标右键点击结束截图 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void ScreenBody_MouseClick(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Right) { this.DialogResult = DialogResult.OK; this.Close(); } }
最复杂的热键注册 自己也是去网上看的 Main窗体中
- 声明枚举
[FlagsAttribute] public enum KeyModifiers { None = 0, Alt = 1, Ctrl = 2, Shift = 4, WindowsKey = 8 }
然后在类中编辑一下代码
-
//在C#中引用命名空间System.Runtime.InteropServices;来加载非托管类user32.dll /* * RegisterHotKey函数原型及说明: * BOOL RegisterHotKey( * HWND hWnd, // window to receive hot-key notification * int id, // identifier of hot key * UINT fsModifiers, // key-modifier flags * UINT vk // virtual-key code); * 参数 id为你自己定义的一个ID值 * 对一个线程来讲其值必需在0x0000 - 0xBFFF范围之内,十进制为0~49151 * 对DLL来讲其值必需在0xC000 - 0xFFFF 范围之内,十进制为49152~65535 * 在同一进程内该值必须唯一参数 fsModifiers指明与热键联合使用按键 * 可取值为:MOD_ALT MOD_CONTROL MOD_WIN MOD_SHIFT参数,或数字0为无,1为Alt,2为Control,4为Shift,8为Windows * vk指明热键的虚拟键码 */ [System.Runtime.InteropServices.DllImport("user32.dll")] //申明API函数 public static extern bool RegisterHotKey( IntPtr hWnd, // handle to window int id, // hot key identifier uint fsModifiers, // key-modifier options Keys vk // virtual-key code ); [System.Runtime.InteropServices.DllImport("user32.dll")] //申明API函数 public static extern bool UnregisterHotKey( IntPtr hWnd, // handle to window int id // hot key identifier );
再接着
private void Form1_Load(object sender, EventArgs e) { uint ctrlHotKey = (uint)(KeyModifiers.Alt | KeyModifiers.Ctrl); // 注册热键为Alt+Ctrl+C, "100"为唯一标识热键 RegisterHotKey(Handle, 100, ctrlHotKey, Keys.A); } //热键按下执行的方法 private void GlobalKeyProcess() { this.WindowState = FormWindowState.Minimized; //窗口最小化需要一定的时间 使用线程 Thread.Sleep(200); btnCutter.PerformClick(); } 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; default: break; } base.WndProc(ref m); } private void Form1_FormClosing(object sender, FormClosingEventArgs e) { // 卸载热键 UnregisterHotKey(Handle, 100); }
热键的功能就能实现。但是我遇到了很多问题 首先是basemap 没有初始化值
- 这些问题 还有待解决!!!