首页 > 解决方案 > 使用来自 OnMouseMove 的 froms OnPaint 方法中的图形绘制时的 C# 大延迟

问题描述

我的目标是在表单上绘制图像(屏幕截图)并使用橡皮筋矩形突出显示选定区域。我希望图像变暗并且选择区域正常。
我试图通过使用鼠标事件来获取用户选择的区域,在表单上绘制图像,然后使用区域(用于变暗/突出显示效果)在图像上绘制不透明的黑色叠加层来实现这一点,除了使用以下代码选择区域:

class ExampleForm : Form {

    private Bitmap screenshot;
    private Rectangle r;
    private Region reg;
    private SolidBrush brush;

    public ExampleForm() {
        r = Screen.PrimaryScreen.Bounds;
        screenshot = new Bitmap(r.Width, r.Height);
        Graphics.FromImage(screenshot).CopyFromScreen(r.Left, r.Top, 0, 0, r.Size);

        SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true);
        FormBorderStyle = FormBorderStyle.None;
        WindowState = FormWindowState.Maximized;

        TopMost = true;
        DoubleBuffered = true;
        ShowInTaskbar = false;

        reg = new Region(r);
        brush = new SolidBrush(Color.FromArgb(200, Color.Black));
    }

    private Point startDrag, endDrag;
    private bool mouseDown;

    protected override void OnMouseDown(MouseEventArgs e) {
        mouseDown = true;
        startDrag = e.Location;
    }

    protected override void OnMouseUp(MouseEventArgs e) {
        mouseDown = false;
    }

    protected override void OnMouseMove(MouseEventArgs e) {
        if (!mouseDown) return;
        endDrag = e.Location;
        UpdatePaint();
    }

    private void UpdatePaint() {
        Rectangle window = new Rectangle(
            Math.Min(startDrag.X, endDrag.X),
            Math.Min(startDrag.Y, endDrag.Y),
            Math.Abs(startDrag.X - endDrag.X),
            Math.Abs(startDrag.Y - endDrag.Y)
            );

        reg = new Region(ClientRectangle);
        reg.Xor(window);
        Invalidate();
    }

    protected override void OnPaint(PaintEventArgs e) {
        Graphics g = e.Graphics;
        g.DrawImage(screenshot, r);
        g.FillRegion(brush, reg);
    }
}

但是,这样做时,在我移动鼠标和“突出显示”区域更新到鼠标位置之间存在相当大的延迟。
我试图在没有图像的情况下进行区域突出显示,甚至只是在没有图像和区域的情况下绘制一个矩形,并且延迟更少,但是与 Windows 资源管理器突出显示或 lightshot 之类的东西相比(这就是我想要的功能重新创建)在鼠标移动和表单中显示的内容之间仍然存在更多延迟。
我什至尝试使用两种形式:一种用于图像,另一种用于变暗/高亮效果,因此不必每次都重新绘制图像,但无法在顶部绘制不透明颜色的同时使表单透明。
我也尝试过 OnPaintBackground,使用标签或图像框,但都有相同的延迟。

那么有没有更有效的方法来进行绘图和/或我使用的突出显示效果?

标签: c#mouseeventhighlightonpaintrubber-band

解决方案


基于 Taw 的评论,这里我们将BackgroundImageForm 设置为屏幕截图的深色版本,并且我们只OnPaint()在当前选择的位置绘制原始版本(“变亮”版本,不是真的)。我还将代码简化为完成任务所需的最低限度:

public partial class ExampleForm : Form
{

    private Point startDrag;
    private SolidBrush brush;
    private Rectangle selectionRc;
    private Bitmap originalScreenShot;

    public ExampleForm()
    {
        this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true);
        this.FormBorderStyle = FormBorderStyle.None;
        this.WindowState = FormWindowState.Maximized;

        this.TopMost = true;
        this.DoubleBuffered = true;
        this.ShowInTaskbar = false;

        this.brush = new SolidBrush(Color.FromArgb(200, Color.Black));

        Rectangle screenRc = Screen.PrimaryScreen.Bounds;
        this.originalScreenShot = new Bitmap(screenRc.Width, screenRc.Height);
        using (Graphics G = Graphics.FromImage(this.originalScreenShot))
        {
            G.CopyFromScreen(screenRc.Left, screenRc.Top, 0, 0, screenRc.Size);
        }

        Bitmap darkenedScreenshot = new Bitmap(this.originalScreenShot);
        using (Graphics G = Graphics.FromImage(darkenedScreenshot))
        {
            G.FillRectangle(this.brush, screenRc);
        }
        this.BackgroundImageLayout = ImageLayout.None;
        this.BackgroundImage = darkenedScreenshot;                       
    }        

    protected override void OnMouseDown(MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Left)
        {
            this.startDrag = e.Location;
        }            
    }

    protected override void OnMouseUp(MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Left)
        {
            // ... do something with "selectionRc" in here? ...
        }            
    }

    protected override void OnMouseMove(MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Left)
        {
            this.selectionRc = new Rectangle(
                Math.Min(this.startDrag.X, e.Location.X),
                Math.Min(this.startDrag.Y, e.Location.Y),
                Math.Abs(this.startDrag.X - e.Location.X),
                Math.Abs(this.startDrag.Y - e.Location.Y)
                );

            this.Invalidate();
        }
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        Graphics g = e.Graphics;
        g.DrawImage(this.originalScreenShot, this.selectionRc, this.selectionRc, GraphicsUnit.Pixel);
    }

}

推荐阅读