重绘时如何修复面板闪烁?

时间:2013-06-02 12:50:23

标签: c# panel

我有一个我已经子类化并且设置DoubleBuffered为真的面板,我经常需要刷新绘图但是它会闪烁并且不知道为什么。

private delegate void MyDelegate();

public void heartBeat()
    {
        while (true)
        {
            if (map.processNubots(rules))
            {
                if (this.InvokeRequired)
                {
                    this.Invoke((MyDelegate)delegate
                    {
                        //drawPanel.SuspendLayout();
                        drawPanel.Refresh();
                        displayGrid();
                        //drawPanel.ResumeLayout();
                    });
                }
                Thread.Sleep(500);
            }
            else
            {
                break;
            }
        }
    }

    public void displayGrid()
    {
        int i = 0;
        foreach (DictionaryEntry pair in map)
        {
            Monomer current = (Monomer)pair.Value;
            drawMonomers(current.getLocation(), current.getState());
            i++;
        }
    }

    public void drawMonomers(Point location, string state)
    {
        ...

        SolidBrush sb = new SolidBrush(mycolor);
        SolidBrush sbt = new SolidBrush(Color.Black);
        Graphics g = drawPanel.CreateGraphics();
        Font text = new Font("Arial", scale / 2);
        Pen pen = new Pen(Color.Black, 1);
        pen.Alignment = PenAlignment.Inset;
        g.FillEllipse(sb, offSet + ((location.Y * scale) / 2) + (location.X * scale), offSet + (-location.Y * scale), scale, scale);
        g.DrawEllipse(pen, offSet + ((location.Y * scale) / 2) + (location.X * scale), offSet + (-location.Y * scale), scale, scale);
        g.DrawString(state, text, sbt, (offSet + ((location.Y * scale) / 2) + (location.X * scale)) + scale / 6, (offSet + (-location.Y * scale)) + scale / 6);

        sb.Dispose();
        sbt.Dispose();
        pen.Dispose();
    }

因此,在每次“计算”之后并向我想象中的网格添加了一些东西,我需要更新面板以在我的网格上显示这个新项目。我已经尝试在displayGrid()函数之前使面板无效,但它似乎会导致更多的闪烁。

当前正在一个单独的线程上调用heartbeat()函数。

这是我的新Panel课程。

public class Display : Panel
{
    public Display()
    {
        this.DoubleBuffered = true;

    }
}

3 个答案:

答案 0 :(得分:9)

    Graphics g = drawPanel.CreateGraphics();

使用CreateGraphics()启用双缓冲是最糟糕的组合。 CreateGraphics()为您提供直接绘制到屏幕的Graphics对象。 Double-buffering设置一个Graphics对象,该对象绘制到一个位图,即双缓冲中使用的缓冲区。然后在绘制周期结束时将位图渲染到屏幕。

所以你的代码中发生的事情是你直接绘制屏幕,​​你几乎看不到它,但是如果它足够慢的话就可见。然后就在那之后,你从未画过的缓冲区会被绘制掉。这抹去了你之前画的东西。净效果是闪烁,您的绘画输出只能看到几毫秒。

使用CreateGraphics()是错误的。 总是想要通过您从Paint事件获得的e.Graphics对象进行渲染,以便渲染到缓冲区。将Graphics对象传递给drawMonomers()方法。因此:

public void drawMonomers(Graphics g, Point location, string state) {
   // Etc...
}

private void Display1_Paint(object sender, PaintEventArgs e) {
   //...
   drawMonomers(e.Graphics, loc, state);
}

通常,CreateGraphics()具有非常有限的用途。当你想要直接绘制到屏幕时,你只能使用它,你可以买得起你想要消失的任何东西。这通常仅适用于具有不断运行的渲染循环的程序,以高速率生成新输出,例如每秒20帧以上。就像一个视频游戏。

答案 1 :(得分:0)

添加AllPaintingInWmPaint样式将阻止重绘背景。

我以前遇到过这篇文章,发现它非常有帮助。 How do I enable double-buffering of a control using C# (Windows forms)?

可能过度杀伤,但它有效。我注意到用户的一件事是,如果 运行得更顺畅,更快,就是。 (即使确实需要更长的时间)

答案 2 :(得分:0)

尝试用PictureBox替换面板。这对我有用。