C#绘图程序闪烁问题

6
我正在尝试用C#制作一个简单的画图程序,但是在绘制时它会闪烁,就像我需要某种双缓冲一样,但我不知道如何实现。
我正在一个Panel上绘制,并使用一个Bitmap来存储图形。
以下是我的代码:
public partial class Form1 : Form
{
    private Bitmap drawing;
    private bool leftDown = false;
    private int prevX;
    private int prevY;
    private int currentX;
    private int currentY;

    public Form1()
    {
        InitializeComponent();

        drawing = new Bitmap(panel1.Width, panel1.Height);
    }

    private void panel1_MouseDown(object sender, MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Left)
        {
            leftDown = true;

            prevX = e.X;
            prevY = e.Y;
        }
    }

    private void panel1_MouseMove(object sender, MouseEventArgs e)
    {
        if (leftDown)
        {
            Graphics g = Graphics.FromImage(drawing);

            currentX = e.X;
            currentY = e.Y;

            g.DrawLine(new Pen(Color.Black), new Point(currentX, currentY), new Point(prevX, prevY));
            panel1.Invalidate();

            prevX = currentX;
            prevY = currentY;

            g.Dispose();
        }
    }

    private void panel1_MouseUp(object sender, MouseEventArgs e)
    {
        leftDown = false;
    }

    private void panel1_MouseLeave(object sender, EventArgs e)
    {
        leftDown = false;
    }

    private void panel1_Paint(object sender, PaintEventArgs e)
    {
        e.Graphics.DrawImageUnscaled(drawing, 0, 0);
    }
}

7
我认为你要找的词是“闪烁”。如果你的电脑在抽搐,也许你应该更加友好地对待它。 - Chris Eberle
使用双缓冲面板:https://dev59.com/yE7Sa4cB1Zd3GeqP5ac4#3113515 - Hans Passant
4个回答

6
你不仅应该将DoubleBuffered设置为true,还应该使用PictureBox代替Panel并在其上进行绘制。这样应该可以解决你的问题 :)。

2
您需要子类化Panel来完成此操作,因为您需要覆盖某些内容。像这样的Panel应该可以工作:
class DoubleBufferedPanel : Panel {
    public DoubleBufferedPanel() : base() {
        this.SetStyle(ControlStyles.AllPaintingInWmPaint |
                      ControlStyles.DoubleBuffered |
                      ControlStyles.Opaque |
                      ControlStyles.OptimizedDoubleBuffer, true);
    }
    public override void OnPaint(PaintEventArgs e) {
        // Do your painting *here* instead, and don't call the base method.
    }

    // Override OnMouseMove, etc. here as well.
}

然而,您根本不需要Panel添加到Control的功能,除非它作为容器正常工作。因此,实际上,除非您需要子控件,否则应该从Control继承。
另一个改进可能是仅使用更改的Rectangle进行Invalidate。这将重新绘制一个区域并减少绘制时间。如果子类化Panel无法满足您的需求,您还可以通过向Graphics.DrawImage传递srcRect(该srcRecte.ClipRectangle计算得出)来获得更好的性能。请注意保留HTML标签。

0
在 Paint 事件中绘制像素地图时,闪烁通常是由于 Windows 窗体先绘制背景,然后再绘制像素地图。因此,闪烁是背景在短暂时间内变得可见的结果。
您可以在面板的 ControlStyle 属性中设置 Opaque 样式。这将关闭背景绘制,因为 Windows 窗体现在假定您的代码将完全绘制面板的内容。

0
通常情况下,只需在您的代码中添加this.DoubleBuffered = true;即可解决问题。
如果仍然无法解决问题,请将以下代码添加到您的窗体中:
protected override CreateParams CreateParams
{
    get
    {
        CreateParams cp = base.CreateParams;
        cp.ExStyle |= 0x02000000;
        return cp;
    }
}

你也可以使用反射来改变它,但是既没有可读性也没有速度上的优势。


但我是在面板上绘制,而不是在窗体上,所以 this.DoubleBuffered = true 似乎行不通。面板没有那个属性。 - Johan Berg
1
算了吧,哦WPF,我总是会忘记它。这很奇怪,因为它使用DirectX,应该自动双缓冲。 - pikzen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接