截图小工具开发笔记
程序员文章站
2022-07-01 23:18:57
一、开发环境及工具 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(); } }
这样截图功能就已经实现了。源代码下载,请