0

我一直在用 C# 制作一个应用程序,旨在模仿 Windows 的 Snipping Tool 应用程序的外观和行为。一切都很好,除了双重缓冲(停止屏幕闪烁)显得缓慢而滞后。延迟并不多,但足以引起注意,尤其是在比较我的程序和 Snipping Tool 之间的性能时。我还能做些什么来提高性能并让它看起来没有延迟,就像在截图工具中一样?

    public Image Image { get; set; }

    private Rectangle selection;
    private Point startPoint;

    public static Image Snip()
    {
        using (var bmp = new Bitmap(SystemInformation.VirtualScreen.Width, SystemInformation.VirtualScreen.Height, System.Drawing.Imaging.PixelFormat.Format32bppPArgb))
        {
            using (var graphics = Graphics.FromImage(bmp)) graphics.CopyFromScreen(SystemInformation.VirtualScreen.Left, SystemInformation.VirtualScreen.Top, 0, 0, bmp.Size);
            using (var snipper = new CaptureScreen(bmp, new Point(SystemInformation.VirtualScreen.Left, SystemInformation.VirtualScreen.Top)))
            {
                if (snipper.ShowDialog() == DialogResult.OK) return snipper.Image;
            }
            return null;
        }
    }

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

        Cursor = Cursors.Cross;
        BackgroundImage = screenShot;
        ShowInTaskbar = false;
        FormBorderStyle = FormBorderStyle.None;
        StartPosition = FormStartPosition.Manual;
        Size = screenShot.Size;
        Location = startPos;
        SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true);
    }

    protected override void OnMouseDown(MouseEventArgs e)
    {
        if (e.Button != MouseButtons.Left) return;
        startPoint = e.Location;
        selection = new Rectangle(e.Location, new Size(0, 0));
        Invalidate();
    }

    protected override void OnMouseMove(MouseEventArgs e)
    {
        if (e.Button != MouseButtons.Left) return;
        var x1 = Math.Min(e.X, startPoint.X);
        var y1 = Math.Min(e.Y, startPoint.Y);
        var x2 = Math.Max(e.X, startPoint.X);
        var y2 = Math.Max(e.Y, startPoint.Y);
        selection = new Rectangle(x1, y1, x2 - x1, y2 - y1);
        Invalidate();
    }

    protected override void OnMouseUp(MouseEventArgs e)
    {
        if (selection.Width <= 0 || selection.Height <= 0) return;
        Image = new Bitmap(selection.Width, selection.Height);
        using (var gr = Graphics.FromImage(Image))
        {
            gr.DrawImage(BackgroundImage, new Rectangle(0, 0, Image.Width, Image.Height),
                selection, GraphicsUnit.Pixel);
        }
        DialogResult = DialogResult.OK;
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        using (var br = new SolidBrush(Color.FromArgb(127, Color.White)))
        {
            var x1 = selection.X;
            var x2 = selection.X + selection.Width;
            var y1 = selection.Y;
            var y2 = selection.Y + selection.Height;

            e.Graphics.FillRectangle(br, new Rectangle(0, 0, x1, Height));
            e.Graphics.FillRectangle(br, new Rectangle(x2, 0, Width - x2, Height));
            e.Graphics.FillRectangle(br, new Rectangle(x1, 0, x2 - x1, y1));
            e.Graphics.FillRectangle(br, new Rectangle(x1, y2, x2 - x1, Height - y2));
        }
        using (var pen = new Pen(Color.Red, 1))
        {
            e.Graphics.DrawRectangle(pen, selection);
        }
    }

我认为它以这种方式滞后的原因是因为应用程序创建了一个屏幕截图并调整了捕获窗口的大小以匹配所有屏幕的尺寸。我有一种感觉,这也是 Snipping Tool 所做的,但它仍然执行得更快。

4

2 回答 2

0

而不是使用Invalidate()which 将使您的控件的整个绘画区域在每次执行鼠标移动时都无效......从而导致完成更多绘画工作......您可以通过仅使需要更改的区域无效来提高效率通过Invalidate()与指定的Rectangle.

什么时候OnPaint被调用............e.ClipRectangle相当于需要更新的“脏”区域......您可以使用它来优化您在其中所做的绘画工作OnPaint

您还可以缓存红色Pen和白色的创建,SolidBrush而不是每次OnPaint发生时都创建它们……您可能会或可能不会看到这样做的任何区别。

这是您的示例的更新:

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;

namespace WindowsFormsApplication3
{
    public partial class CaptureScreen : Form
    {
        public Image Image { get; set; }

        private Rectangle selection;
        private Rectangle previousselection;
        private Point startPoint;

        public static Image Snip()
        {
            using (var bmp = new Bitmap(SystemInformation.VirtualScreen.Width, SystemInformation.VirtualScreen.Height, System.Drawing.Imaging.PixelFormat.Format32bppPArgb))
            {
                using (var graphics = Graphics.FromImage(bmp)) graphics.CopyFromScreen(SystemInformation.VirtualScreen.Left, SystemInformation.VirtualScreen.Top, 0, 0, bmp.Size);
                using (var snipper = new CaptureScreen(bmp, new Point(SystemInformation.VirtualScreen.Left, SystemInformation.VirtualScreen.Top)))
                {
                    if (snipper.ShowDialog() == DialogResult.OK) return snipper.Image;
                }
                return null;
            }
        }

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

            Cursor = Cursors.Cross;
            BackgroundImage = screenShot;
            ShowInTaskbar = false;
            FormBorderStyle = FormBorderStyle.None;
            StartPosition = FormStartPosition.Manual;
            Size = screenShot.Size;
            Location = startPos;
            SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true);
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            if (e.Button != MouseButtons.Left) return;
            startPoint = e.Location;
            selection = new Rectangle(e.Location, new Size(0, 0));
            previousselection = selection;
            Invalidate();
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            if (e.Button != MouseButtons.Left) return;
            var x1 = Math.Min(e.X, startPoint.X);
            var y1 = Math.Min(e.Y, startPoint.Y);
            var x2 = Math.Max(e.X, startPoint.X);
            var y2 = Math.Max(e.Y, startPoint.Y);
            Invalidate(previousselection); // invalidate old rect area so it gets blanked out
            previousselection = selection;
            selection = new Rectangle(x1, y1, x2 - x1, y2 - y1);
            Invalidate(selection); // invalidate new rect area so it gets drawn
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            if (selection.Width <= 0 || selection.Height <= 0) return;
            Image = new Bitmap(selection.Width, selection.Height);
            using (var gr = Graphics.FromImage(Image))
            {
                gr.DrawImage(BackgroundImage, new Rectangle(0, 0, Image.Width, Image.Height),
                    selection, GraphicsUnit.Pixel);
            }
            DialogResult = DialogResult.OK;
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            using (var br = new SolidBrush(Color.FromArgb(127, Color.White)))
            using (var pen = new Pen(Color.Red, 1))
            using (Region region = new Region(new Rectangle(0,0,Width,Height)))
            {
                region.Exclude(selection);
                region.Intersect(e.ClipRectangle);
                e.Graphics.FillRegion(br, region);
                e.Graphics.DrawRectangle(pen, selection.X, selection.Y, selection.Width - 1, selection.Height - 1);
            }
        }
    }
}
于 2013-08-25T00:44:15.530 回答
0

当你Invalidate()使用

public async void name()
{
    await Task.Run(() =>
    {
        Invalidate();
    });
}

asyncawait等待完成,因此它不会全部删除,然后实时绘制。

于 2018-11-14T00:08:22.390 回答