7

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

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;

    }
}
4

3 回答 3

12
    Graphics g = drawPanel.CreateGraphics();

使用 CreateGraphics()打开双缓冲是最糟糕的组合。CreateGraphics() 为您提供一个直接在屏幕上绘制的 Graphics 对象。双缓冲设置了一个绘制位图的 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+ 帧的高速率产生新的输出。就像一个视频游戏。

于 2013-06-02T15:15:54.583 回答
2

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

于 2019-05-23T18:41:19.937 回答
0

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

我以前看过这篇文章,发现它很有帮助。 如何使用 C#(Windows 窗体)启用控件的双缓冲?

可能是矫枉过正,但它的工作原理。我注意到用户的一件事是,如果它看起来运行得更顺畅、更快,那就是. (即使确实需要更长的时间)

于 2013-06-02T13:42:33.870 回答