13

我正在开发一种绘图程序,但在绘制橡皮筋线时移动鼠标光标时出现闪烁问题。我希望你能帮助我消除闪烁的线条,这里是代码:

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 GraphicsTest
{
    public partial class Form1 : Form
    {
        int xFirst, yFirst;
        Bitmap bm = new Bitmap(1000, 1000);
        Graphics bmG;
        Graphics xG;
        Pen pen = new Pen(Color.Black, 1);
        bool draw = false;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            bmG = Graphics.FromImage(bm);
            xG = this.CreateGraphics();
            bmG.Clear(Color.White);
        }

        private void Form1_MouseDown(object sender, MouseEventArgs e)
        {
            xFirst = e.X;
            yFirst = e.Y;
            draw = true;
        }

        private void Form1_MouseUp(object sender, MouseEventArgs e)
        {
            bmG.DrawLine(pen, xFirst, yFirst, e.X, e.Y);
            draw = false;
            xG.DrawImage(bm, 0, 0);
        }

        private void Form1_MouseMove(object sender, MouseEventArgs e)
        {
            if (draw)
            {
                xG.DrawImage(bm, 0, 0);
                xG.DrawLine(pen, xFirst, yFirst, e.X, e.Y);
            }
        }

        private void Form1_Paint(object sender, PaintEventArgs e)
        {
            xG.DrawImage(bm, 0, 0);
        }
    }
}
4

4 回答 4

32

首先不要使用CreateGraphics(),除非你绝对必须。将事件处理程序绑定到OnPaintInvalidate()在您想要刷新表面时调用。

如果您不希望它闪烁,则需要对绘图表面进行双重缓冲。最简单的方法是将表单的DoubleBuffered属性设置为 True。

如果您打算将其扩展为对 PictureBox 控件进行绘图,我强烈建议您这样做。PictureBox 默认是双缓冲的,让您可以更简单地控制绘图区域。

在代码中:

public partial class Form1 : Form
    {
    int xFirst, yFirst;
    Bitmap bm = new Bitmap(1000, 1000);
    Graphics bmG;
    Pen pen = new Pen(Color.Black, 1);
    bool draw = false;

    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        bmG = Graphics.FromImage(bm);
        bmG.Clear(Color.White);
    }

    private void Form1_MouseDown(object sender, MouseEventArgs e)
    {
        xFirst = e.X;
        yFirst = e.Y;
        draw = true;
    }

    private void Form1_MouseUp(object sender, MouseEventArgs e)
    {
        bmG.DrawLine(pen, xFirst, yFirst, e.X, e.Y);
        draw = false;
        Invalidate();
    }

    private void Form1_MouseMove(object sender, MouseEventArgs e)
    {
        if (draw)
        {
            Invalidate();
        }
    }

    private void Form1_Paint(object sender, PaintEventArgs e)
    {
        if (draw) {
            e.Graphics.DrawImage(bm, 0, 0);
            e.Graphics.DrawLine(pen, xFirst, yFirst, e.X, e.Y);
        } else {
            e.Graphics.DrawImage(bm, 0, 0);
        }
    }
}

编辑:

另一个问题,您正在创建一个私人Pen成员。笔(和画笔,以及许多 GDI+ 对象)代表需要释放的非托管对象的句柄,否则您的程序将泄漏。要么将它们包装在using语句中(首选和异常安全的方式),要么在表单的Dispose方法中显式处理它们。

或者,在 System.Drawing 中,您可以访问一些不需要(也不应该)处理的预构建钢笔和画笔。像这样使用它们:

    private void Form1_Paint(object sender, PaintEventArgs e)
    {
        if (draw) {
            e.Graphics.DrawImage(bm, 0, 0);
            e.Graphics.DrawLine(Pens.Black, xFirst, yFirst, e.X, e.Y);
        } else {
            e.Graphics.DrawImage(bm, 0, 0);
        }
    }
于 2010-04-09T16:02:12.780 回答
9

它闪烁的原因是您正在绘制背景(立即显示在屏幕上,擦除线条)然后叠加线条。因此,线条不断消失和出现,显示闪烁。

对此的最佳解决方案称为双缓冲。您所做的是将整个图像绘制到“屏幕外”位图上,并且仅在完成后才显示在屏幕上。因为您只显示完成的图像,所以没有闪烁效果。您应该能够设置 this.DoubleBuffered = true 来让 WinForms 为您完成所有艰苦的工作。

注意:你不应该真的在你的油漆处理程序之外绘图 - 理想情况下,你应该 Invalidate() 需要重绘的区域,然后你的油漆处理程序将只重绘该区域(根据需要使用任何覆盖线等)。

于 2010-04-09T16:03:43.947 回答
2

固定和工作代码。

public partial class Form1 : Form
{
    int x1, y1, x2, y2;
    bool drag = false;

    Bitmap bm = new Bitmap(1000, 1000);
    Graphics bmg;


    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        bmg = Graphics.FromImage(bm);
    }

    private void pictureBox_MouseDown(object sender, MouseEventArgs e)
    {
        drag = true;
        x1 = e.X;
        y1 = e.Y;
    }

    private void pictureBox_MouseUp(object sender, MouseEventArgs e)
    {
        drag = false;

        bmg.DrawLine(Pens.Black, x1, y1, e.X, e.Y);
        pictureBox.Invalidate();
    }

    private void pictureBox_MouseMove(object sender, MouseEventArgs e)
    {
        if (drag)
        {
            x2 = e.X;
            y2 = e.Y;
            pictureBox.Invalidate();
        }
    }

    private void pictureBox_Paint(object sender, PaintEventArgs e)
    {
        if (drag) {
            e.Graphics.DrawImage(bm, 0, 0);
            e.Graphics.DrawLine(Pens.Black, x1, y1, x2, y2);            
        }
        else {
            e.Graphics.DrawImage(bm, 0, 0);
        }
    }
}
于 2010-04-09T17:19:39.147 回答
0

我使用它来管理面板中的双缓冲:

myPanel.GetType().GetMethod("SetStyle",
    System.Reflection.BindingFlags.Instance |
    System.Reflection.BindingFlags.NonPublic).Invoke(myPanel,
        new object[]
        {
            System.Windows.Forms.ControlStyles.UserPaint | 
            System.Windows.Forms.ControlStyles.AllPaintingInWmPaint |
            System.Windows.Forms.ControlStyles.DoubleBuffer, true
        });
于 2020-08-28T10:50:14.810 回答