C#实现QQ截图功能及相关问题
程序员文章站
2023-12-17 20:05:40
对于qq截图,肯定是早就有认识了,只是一直没有去认真观察这个操作的具体实现步骤。所以这里将自己的记忆中的步骤简单的写一下:
习惯性用qq或者tim的人,一般是使用ctrl...
对于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 没有初始化值
这些问题 还有待解决!!!
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。