欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

截图小工具开发笔记

程序员文章站 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;

 

 

此窗口内主要包括2label控件,1combobox1picturebox,摆放位置如上图。label用于显示文字说明;combobox用于显示热键09个数字供选择;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();
            }
        }

 

这样截图功能就已经实现了。源代码下载,请