Laggy剪切工具

时间:2018-05-22 14:03:28

标签: c# .net drawing mouseevent paint

我正在尝试创建像这样的https://app.prntscr.com/en/index.html这样的剪切工具。我能够修改一些代码,并设法显示调整大小句柄与选定的矩形(剪辑),句柄工作正常,我可以移动选定的矩形并调整大小。但是,当我将它与上面提到的工具进行比较时,它非常慢,有人可以指出我的代码中的一些问题,以使其快速?

我正在制作的工具如下所示: enter image description here

    /* 
 * Credit for this portion of Imgur Snipping Tool goes to Hans Passant from stackoverflow.com
 * http://stackoverflow.com/questions/3123776/net-equivalent-of-snipping-tool
 * 
 * Modified to work with multiple monitors.
 */

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using Free_Snipping_Tool.Classes;

namespace Free_Snipping_Tool
{
    public partial class SnippingTool2 : Form
    {
        public enum ScreenshotType
        {
            FULL, ACTIVE, DEFINED
        }
        private int oldX;
        private int oldY;
        private struct RECT
        {
            public int Left;
            public int Top;
            public int Right;
            public int Bottom;
        }

        Rectangle topLeft = new Rectangle();
        Rectangle topMiddle = new Rectangle();
        Rectangle topRight = new Rectangle();
        Rectangle bottomLeft = new Rectangle();
        Rectangle bottomMiddle = new Rectangle();
        Rectangle bottomRight = new Rectangle();
        Rectangle leftMiddle = new Rectangle();
        Rectangle rightMiddle = new Rectangle();

        bool topLeftSelected = false;
        bool topMiddleSelected = false;
        bool topRightSelected = false;
        bool bottomLeftSelected = false;
        bool bottomMiddleSelected = false;
        bool bottomRightSelected = false;
        bool leftMiddleSelected = false;
        bool rightMiddleSelected = false;
        bool rectangleSelected = false;

        [DllImport("user32.dll")]
        private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);

        [DllImport("user32.dll")]
        private static extern int GetForegroundWindow();

        SnipSizeControl lbl = new SnipSizeControl();
        floatingControlRight fcR = new floatingControlRight();
        floatingControlBottom fcB = new floatingControlBottom();

        public static Image Snip(ScreenshotType type)
        {
            switch (type)
            {
                case ScreenshotType.FULL:
                    return CreateScreenshot(0, 0, SystemInformation.VirtualScreen.Width, SystemInformation.VirtualScreen.Height);

                case ScreenshotType.DEFINED:
                    SnippingTool2 snipper = new SnippingTool2(CreateScreenshot(0, 0, SystemInformation.VirtualScreen.Width, SystemInformation.VirtualScreen.Height), new Point(SystemInformation.VirtualScreen.Left, SystemInformation.VirtualScreen.Top));
                    if (snipper.ShowDialog() == DialogResult.OK)
                    {
                        return snipper.Image;
                    }
                    break;
                case ScreenshotType.ACTIVE:
                    RECT windowRectangle;
                    GetWindowRect((System.IntPtr)GetForegroundWindow(), out windowRectangle);
                    return CreateScreenshot(windowRectangle.Left, windowRectangle.Top, windowRectangle.Right - windowRectangle.Left, windowRectangle.Bottom - windowRectangle.Top);
            }
            return null;
        }

        private static Bitmap CreateScreenshot(int left, int top, int width, int height)
        {
            Bitmap bmp = new Bitmap(width, height);
            Graphics g = Graphics.FromImage(bmp);
            g.CopyFromScreen(left, top, 0, 0, new Size(width, height));
            g.Dispose();
            return bmp;
        }


        public SnippingTool2(Image screenShot, Point startPos)
        {
            InitializeComponent();

            lbl.Text = "0x0";
            lbl.Visible = true;
            lbl.BackColor = Color.Black;
            lbl.ForeColor = Color.White;
            lbl.Visible = false;
            this.Controls.Add(lbl);

            this.BackgroundImage = screenShot;
            Size s = screenShot.Size;
            this.ShowInTaskbar = false;
            this.FormBorderStyle = FormBorderStyle.None;
            this.StartPosition = FormStartPosition.Manual;
            this.Size = s;
            this.Location = startPos;
            this.DoubleBuffered = true;
        }
        public Image Image { get; set; }

        private Rectangle rcSelect = new Rectangle();
        private Point pntStart;
        bool handleSelected = false;

        protected override void OnMouseDown(MouseEventArgs e)
        {

            if ((e.X > topLeft.X) && (e.X < topLeft.X + topLeft.Width) && (e.Y > topLeft.Y) && (e.Y < topLeft.Y + topLeft.Height))
            {
                this.Cursor = Cursors.SizeNWSE;
                topLeftSelected = true;
                handleSelected = true;
            }

            else if ((e.X > topMiddle.X) && (e.X < topMiddle.X + topMiddle.Width) && (e.Y > topMiddle.Y) && (e.Y < topMiddle.Y + topMiddle.Height))
            {
                this.Cursor = Cursors.SizeNS;
                topMiddleSelected = true;
                handleSelected = true;
            }

            else if ((e.X > topRight.X) && (e.X < topRight.X + topRight.Width) && (e.Y > topRight.Y) && (e.Y < topRight.Y + topRight.Height))
            {
                this.Cursor = Cursors.SizeNESW;
                topRightSelected = true;
                handleSelected = true;
            }

            else if ((e.X > bottomLeft.X) && (e.X < bottomLeft.X + bottomLeft.Width) && (e.Y > bottomLeft.Y) && (e.Y < bottomLeft.Y + bottomLeft.Height))
            {
                this.Cursor = Cursors.SizeNESW;

                bottomLeftSelected = true;
                handleSelected = true;

            }

            else if ((e.X > bottomMiddle.X) && (e.X < bottomMiddle.X + bottomMiddle.Width) && (e.Y > bottomMiddle.Y) && (e.Y < bottomMiddle.Y + bottomMiddle.Height))
            {
                this.Cursor = Cursors.SizeNS;
                bottomMiddleSelected = true;
                handleSelected = true;
            }

            else if ((e.X > bottomRight.X) && (e.X < bottomRight.X + bottomRight.Width) && (e.Y > bottomRight.Y) && (e.Y < bottomRight.Y + bottomRight.Height))
            {
                this.Cursor = Cursors.SizeNWSE;
                bottomRightSelected = true;
                handleSelected = true;

            }

            else if ((e.X > leftMiddle.X) && (e.X < leftMiddle.X + leftMiddle.Width) && (e.Y > leftMiddle.Y) && (e.Y < leftMiddle.Y + leftMiddle.Height))
            {
                this.Cursor = Cursors.SizeWE;
                leftMiddleSelected = true;
                handleSelected = true;
            }

            else if ((e.X > rightMiddle.X) && (e.X < rightMiddle.X + rightMiddle.Width) && (e.Y > rightMiddle.Y) && (e.Y < rightMiddle.Y + rightMiddle.Height))
            {
                this.Cursor = Cursors.SizeWE;
                rightMiddleSelected = true;
                handleSelected = true;
            }
            else if ((e.X > rcSelect.X) && (e.X < rcSelect.X + rcSelect.Width) && (e.Y > rcSelect.Y) && (e.Y < rcSelect.Y + rcSelect.Height))
            {
                this.Cursor = Cursors.SizeAll;
                rectangleSelected = true;
                handleSelected = true;
            }
            else
            {
                this.Cursor = Cursors.Cross;
                topLeftSelected = false;
                topMiddleSelected = false;
                topRightSelected = false;
                bottomLeftSelected = false;
                bottomMiddleSelected = false;
                bottomRightSelected = false;
                leftMiddleSelected = false;
                rightMiddleSelected = false;
                rectangleSelected = false;
                handleSelected = false;
            }

            // Start the snip on mouse down
            if (e.Button != MouseButtons.Left) return;
            if (handleSelected == true) return;

            pntStart = e.Location;
            rcSelect = new Rectangle(e.Location, new Size(0, 0));
            lbl.Visible = true;

            lbl.Location = new Point(e.X + 10, e.Y + 10); ;
            lbl.Value = string.Format("0x0");
            lbl.Refresh();
            fcR.Visible = false;

            oldX = e.X;
            oldY = e.Y;

            this.Refresh();
        }

        protected override CreateParams CreateParams
        {
            get
            {
                var cp = base.CreateParams;
                cp.ExStyle |= 0x02000000;    // Turn on WS_EX_COMPOSITED
                return cp;
            }
        }
        protected override void OnMouseMove(MouseEventArgs e)
        {
            // Modify the selection on mouse move

            if ((e.X > topLeft.X) && (e.X < topLeft.X + topLeft.Width) && (e.Y > topLeft.Y) && (e.Y < topLeft.Y + topLeft.Height))
            {
                this.Cursor = Cursors.SizeNWSE;
            }

            else if ((e.X > topMiddle.X) && (e.X < topMiddle.X + topMiddle.Width) && (e.Y > topMiddle.Y) && (e.Y < topMiddle.Y + topMiddle.Height))
            {
                this.Cursor = Cursors.SizeNS;
            }

            else if ((e.X > topRight.X) && (e.X < topRight.X + topRight.Width) && (e.Y > topRight.Y) && (e.Y < topRight.Y + topRight.Height))
            {
                this.Cursor = Cursors.SizeNESW;
            }

            else if ((e.X > bottomLeft.X) && (e.X < bottomLeft.X + bottomLeft.Width) && (e.Y > bottomLeft.Y) && (e.Y < bottomLeft.Y + bottomLeft.Height))
            {
                this.Cursor = Cursors.SizeNESW;
            }

            else if ((e.X > bottomMiddle.X) && (e.X < bottomMiddle.X + bottomMiddle.Width) && (e.Y > bottomMiddle.Y) && (e.Y < bottomMiddle.Y + bottomMiddle.Height))
            {
                this.Cursor = Cursors.SizeNS;
            }

            else if ((e.X > bottomRight.X) && (e.X < bottomRight.X + bottomRight.Width) && (e.Y > bottomRight.Y) && (e.Y < bottomRight.Y + bottomRight.Height))
            {
                this.Cursor = Cursors.SizeNWSE;
            }

            else if ((e.X > leftMiddle.X) && (e.X < leftMiddle.X + leftMiddle.Width) && (e.Y > leftMiddle.Y) && (e.Y < leftMiddle.Y + leftMiddle.Height))
            {
                this.Cursor = Cursors.SizeWE;
            }

            else if ((e.X > rightMiddle.X) && (e.X < rightMiddle.X + rightMiddle.Width) && (e.Y > rightMiddle.Y) && (e.Y < rightMiddle.Y + rightMiddle.Height))
            {
                this.Cursor = Cursors.SizeWE;
            }
            else if ((e.X > rcSelect.X) && (e.X < rcSelect.X + rcSelect.Width) && (e.Y > rcSelect.Y) && (e.Y < rcSelect.Y + rcSelect.Height))
            {
                this.Cursor = Cursors.SizeAll;
            }
            else
            {
                this.Cursor = Cursors.Cross;
            }

            if (e.Button == MouseButtons.Left && handleSelected == true)
            {
                Rectangle backupRect = rcSelect;

                if (topLeftSelected == true)
                {
                    rcSelect.X += e.X - oldX;
                    rcSelect.Width -= e.X - oldX;
                    rcSelect.Y += e.Y - oldY;
                    rcSelect.Height -= e.Y - oldY;

                    lbl.Location = new Point(rcSelect.X, rcSelect.Y - 25);
                    lbl.Value = string.Format("{0}x{1}", rcSelect.Width, rcSelect.Height);
                    this.Refresh();
                }

                else if (topRightSelected == true)
                {
                    rcSelect.Width += e.X - oldX;
                    rcSelect.Y += e.Y - oldY;
                    rcSelect.Height -= e.Y - oldY;

                    lbl.Location = new Point(rcSelect.X, rcSelect.Y - 25);
                    lbl.Value = string.Format("{0}x{1}", rcSelect.Width, rcSelect.Height);
                    this.Refresh();
                }

                else if (topMiddleSelected == true)
                {

                    rcSelect.Y += e.Y - oldY;
                    rcSelect.Height -= e.Y - oldY;

                    lbl.Location = new Point(rcSelect.X, rcSelect.Y - 25);
                    lbl.Value = string.Format("{0}x{1}", rcSelect.Width, rcSelect.Height);
                    this.Refresh();

                }

                else if (bottomLeftSelected == true)
                {
                    rcSelect.Width -= e.X - oldX;
                    rcSelect.X += e.X - oldX;
                    rcSelect.Height += e.Y - oldY;

                    lbl.Location = new Point(rcSelect.X, rcSelect.Y - 25);
                    lbl.Value = string.Format("{0}x{1}", rcSelect.Width, rcSelect.Height);
                    this.Refresh();
                }
                else if (bottomMiddleSelected == true)
                {
                    rcSelect.Height += e.Y - oldY;

                    lbl.Location = new Point(rcSelect.X, rcSelect.Y - 25);
                    lbl.Value = string.Format("{0}x{1}", rcSelect.Width, rcSelect.Height);
                    this.Refresh();
                }
                else if (bottomRightSelected == true)
                {
                    rcSelect.Width += e.X - oldX;
                    rcSelect.Height += e.Y - oldY;

                    lbl.Location = new Point(rcSelect.X, rcSelect.Y - 25);
                    lbl.Value = string.Format("{0}x{1}", rcSelect.Width, rcSelect.Height);
                    this.Refresh();
                }
                else if (rightMiddleSelected == true)
                {
                    rcSelect.Width += e.X - oldX;

                    lbl.Location = new Point(rcSelect.X, rcSelect.Y - 25);
                    lbl.Value = string.Format("{0}x{1}", rcSelect.Width, rcSelect.Height);
                    this.Refresh();
                }
                else if (leftMiddleSelected == true)
                {
                    rcSelect.X += e.X - oldX;
                    rcSelect.Width -= e.X - oldX;

                    lbl.Location = new Point(rcSelect.X, rcSelect.Y - 25);
                    lbl.Value = string.Format("{0}x{1}", rcSelect.Width, rcSelect.Height);
                    this.Refresh();
                }
                else if (rectangleSelected == true)
                {
                    rcSelect.X = rcSelect.X + e.X - oldX;
                    rcSelect.Y = rcSelect.Y + e.Y - oldY;

                    lbl.Location = new Point(rcSelect.X, rcSelect.Y - 25);
                    lbl.Value = string.Format("{0}x{1}", rcSelect.Width, rcSelect.Height);
                    this.Refresh();
                }
            }
            else if (e.Button == MouseButtons.Left)
            {
                int x1 = Math.Min(e.X, pntStart.X);
                int y1 = Math.Min(e.Y, pntStart.Y);
                int x2 = Math.Max(e.X, pntStart.X);
                int y2 = Math.Max(e.Y, pntStart.Y);

                rcSelect = new Rectangle(x1, y1, x2 - x1, y2 - y1);

                this.Refresh();

                lbl.Location = new Point(x1, y1 - 25);
                lbl.Value = string.Format("{0}x{1}", x2 - x1, y2 - y1);
                lbl.Refresh();

            }

            oldX = e.X;
            oldY = e.Y;

        }
        protected override void OnMouseUp(MouseEventArgs e)
        {
            // Complete the snip on mouse-up
            if (rcSelect.Width <= 0 || rcSelect.Height <= 0) return;
            Image = new Bitmap(rcSelect.Width, rcSelect.Height);
            using (Graphics gr = Graphics.FromImage(Image))
            {
                gr.DrawImage(this.BackgroundImage, new Rectangle(0, 0, Image.Width, Image.Height),
                    rcSelect, GraphicsUnit.Pixel);
            }

            topLeftSelected = false;
            topMiddleSelected = false;
            topRightSelected = false;
            bottomLeftSelected = false;
            bottomMiddleSelected = false;
            bottomRightSelected = false;
            leftMiddleSelected = false;
            rightMiddleSelected = false;
            rectangleSelected = false;
            handleSelected = false;

            //DialogResult = DialogResult.OK;
        }
        protected override void OnPaint(PaintEventArgs e)
        {
            // Draw the current selection
            using (Brush br = new SolidBrush(Color.FromArgb(30, Color.Black)))
            {
                int x1 = rcSelect.X; int x2 = rcSelect.X + rcSelect.Width;
                int y1 = rcSelect.Y; int y2 = rcSelect.Y + rcSelect.Height;
                e.Graphics.FillRectangle(br, new Rectangle(0, 0, x1, this.Height));
                e.Graphics.FillRectangle(br, new Rectangle(x2, 0, this.Width - x2, this.Height));
                e.Graphics.FillRectangle(br, new Rectangle(x1, 0, x2 - x1, y1));
                e.Graphics.FillRectangle(br, new Rectangle(x1, y2, x2 - x1, this.Height - y2));
            }
            using (Pen pen = new Pen(Color.Red, 1))
            {
                e.Graphics.DrawRectangle(pen, rcSelect);
            }


            //Resize Controls

            //Top left
            topLeft = new Rectangle(rcSelect.X - 3, rcSelect.Y - 3, 6, 6);
            using (Brush br = new SolidBrush(Color.FromArgb(200, Color.Red)))
            {
                e.Graphics.FillRectangle(br, topLeft);
            }

            //Top middle
            topMiddle = new Rectangle((rcSelect.X - 3) + (rcSelect.Width) / 2, rcSelect.Y - 3, 6, 6);
            using (Brush br = new SolidBrush(Color.FromArgb(200, Color.Red)))
            {
                e.Graphics.FillRectangle(br, topMiddle);
            }

            //Top right
            topRight = new Rectangle((rcSelect.X - 3) + rcSelect.Width, rcSelect.Y - 3, 6, 6);
            using (Brush br = new SolidBrush(Color.FromArgb(200, Color.Red)))
            {
                e.Graphics.FillRectangle(br, topRight);
            }


            //Bottom left
            bottomLeft = new Rectangle(rcSelect.X - 3, (rcSelect.Y - 3) + rcSelect.Height, 6, 6);
            using (Brush br = new SolidBrush(Color.FromArgb(200, Color.Red)))
            {
                e.Graphics.FillRectangle(br, bottomLeft);
            }

            //Bottom middle
            bottomMiddle = new Rectangle((rcSelect.X - 3) + (rcSelect.Width) / 2, (rcSelect.Y - 3) + rcSelect.Height, 6, 6);
            using (Brush br = new SolidBrush(Color.FromArgb(200, Color.Red)))
            {
                e.Graphics.FillRectangle(br, bottomMiddle);
            }

            //Bottom right
            bottomRight = new Rectangle((rcSelect.X - 3) + rcSelect.Width, (rcSelect.Y - 3) + rcSelect.Height, 6, 6);
            using (Brush br = new SolidBrush(Color.FromArgb(200, Color.Red)))
            {
                e.Graphics.FillRectangle(br, bottomRight);
            }

            //Left middle
            leftMiddle = new Rectangle(rcSelect.X - 3, (rcSelect.Y - 3) + (rcSelect.Height) / 2, 6, 6);
            using (Brush br = new SolidBrush(Color.FromArgb(200, Color.Red)))
            {
                e.Graphics.FillRectangle(br, leftMiddle);
            }

            //Right middle
            rightMiddle = new Rectangle((rcSelect.X - 3) + rcSelect.Width, (rcSelect.Y - 3) + (rcSelect.Height) / 2, 6, 6);
            using (Brush br = new SolidBrush(Color.FromArgb(200, Color.Red)))
            {
                e.Graphics.FillRectangle(br, rightMiddle);
            }
        }
        protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
        {
            // Allow canceling the snip with the Escape key
            if (keyData == Keys.Escape) this.DialogResult = DialogResult.Cancel;
            return base.ProcessCmdKey(ref msg, keyData);
        }
    }
}

修改 项目文件:https://www.dropbox.com/s/k1afggj6inye4kp/Free%20Snipipng%20Tool-project.rar?dl=0

1 个答案:

答案 0 :(得分:1)

我会引用官方Windows documentation。尽管该文档是关于Native API的,但Winforms使用相同的基础技术:

  

应用程序使窗口的一部分无效并设置更新   使用InvalidateRect或InvalidateRgn函数的区域。这些   函数将指定的矩形或区域添加到更新区域,   将矩形或区域与系统或任何系统相结合   应用程序可能先前已添加到更新区域。

     

不会生成InvalidateRect和InvalidateRgn函数   WM_PAINT消息。相反,系统会累积所做的更改   这些功能和它自己的变化。通过累积变化,一个窗口   一次处理所有更改,而不是更新一点一点   一步一步。

调用.NET Refresh()相当于调用InvalidateAll() + Update()InvalidateAll将整个屏幕标记为无效,Update()强制重绘其无效的过程,因此整个屏幕都是如此。 如果您只是手动&#34;无效,您可以优化您的程序。你所知道的已经改变了。

这就是我在修改过的样本中所做的。我没有调用Refresh(),而是添加了一个新的oldRcRect变量,以便能够使旧的状态和新状态无效,以及像这样的RefreshOnMove()方法(我通过RefreshOnMove调用替换了大多数Refresh调用): / p>

    private void RefreshOnMove()
    {
        // invalidate the old rect (+ size of red box)
        var rc = oldRcSelect;
        rc.Inflate(3, 3);
        Invalidate(rc);

        // invalidate the new rect (+ size of red box)
        // note you can almost omit this second one, but if you move the mouse really fast, you'll see some red box not fully displayed
        // but the benefit is small, something like a 3 x width/height rectangle
        rc = rcSelect;
        rc.Inflate(3, 3);
        Invalidate(rc);

        // each time you call invalidate, you just accumulate a change
        // to the change region, nothing actually changes on the screen

        // now, ask Windows to process the combination of changes
        Update();
    }

PS:关于我对内部区域的评论,我只是说每次都可以避免使白盒内容无效,但它更复杂。