c# - 滞后的截图工具

标签 c# .net drawing mouseevent paint

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

我正在制作的工具如下所示: 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

最佳答案

我会引用官方Windows documentation .尽管该文档是关于 native API 的,但 Winforms 使用相同的底层技术:

An application invalidates a portion of a window and sets the update region by using the InvalidateRect or InvalidateRgn function. These functions add the specified rectangle or region to the update region, combining the rectangle or region with anything the system or the application may have previously added to the update region.

The InvalidateRect and InvalidateRgn functions do not generate WM_PAINT messages. Instead, the system accumulates the changes made by these functions and its own changes. By accumulating changes, a window processes all changes at once instead of updating bits and pieces one step at a time.

调用 .NET Refresh() 等同于调用 InvalidateAll() + Update()InvalidateAll 将整个屏幕标记为无效,Update() 强制重新绘制无效内容的过程,因此整个屏幕。 如果您只是“手动”使您知道已更改的内容无效,则可以优化您的程序。

这就是我在修改后的示例中所做的。我没有调用 Refresh(),而是添加了一个新的 oldRcRect 变量来使旧状态和新状态失效,还有一个像这样的 RefreshOnMove() 方法(我用 RefreshOnMove 调用替换了大多数 Refresh 调用):

    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:关于我对内部区域的评论,我的意思是可能也可以避免每次都使白框的内容无效,但它更复杂。

关于c# - 滞后的截图工具,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50469711/

相关文章:

c# - 为什么等待这个错误任务不抛出异常?

c# - 将数据发布到 asp.net 网页时出现问题(Desktop C# 端的问题)

c# - 在 Intellisense 中将属性/函数设为粗体

java - 在 Java 中绘制屏幕

c# - 如何使用 Compact Framework 垂直绘制文本

android - 如何绘制矩形对象的网格?

c# - 如何使用 Ninject.Extensions.Logging 在 void Main 中获取 Logger?

c# - Web API 中的 LINQ 时间减法

.net - 如何在.Net客户端接收时删除WebSphere MQ分组消息中的消息头信息

C# LINQ 创建月份和年份列表