Winforms 图形闪烁。 (双缓冲没有帮助!)

Winforms Graphics flickering. (Double buffering doesn't help!)

我正在尝试创建一个简单的 Windows 表单图形应用程序,它基本上会在每次用户单击时绘制一个圆圈,圆圈会扩大,同时慢慢消失。

当我尝试将 Paint() 事件用于我的图形功能时,没有任何反应,因此我创建了一个名为 "Render" 的单独函数,该函数在我的主要更新 Timer 中调用。

应用程序运行正常,但图形闪烁。经过一番研究后,我意识到我必须启用双缓冲,以便它可以呈现到缓冲区,然后缓冲区将呈现到屏幕。

闪烁仍然没有停止!
这是因为双缓冲仅适用于 Paint() 事件吗?如果是这样,我如何让 Paint() 事件起作用,或者我是否没有启用双缓冲?

这是我的代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Widget
{

public class Circle
{
    public float X;
    public float Y;
    public float Radius;
    public int Alpha;

    public Circle(float X, float Y, float Radius, int Alpha)
    {
        this.X = X;
        this.Y = Y;
        this.Radius = Radius;
        this.Alpha = Alpha;
    }
}

public partial class Form1 : Form
{
    public static readonly int ScreenX = Screen.PrimaryScreen.Bounds.Width;
    public static readonly int ScreenY = Screen.PrimaryScreen.Bounds.Height;

    public int WindowWidth = 500, WindowHeight = 500;
    public Graphics G;
    private Pen Pen;

    private Timer timer = new Timer();
    private List<Circle> Circles;

    public Form1()
    {
        this.Text = "Widget - Sam Brandt";
        this.Size = new Size(WindowWidth, WindowHeight);
        this.StartPosition = FormStartPosition.Manual;
        this.Location = new Point(ScreenX - WindowWidth - 100, 0);
        this.FormBorderStyle = FormBorderStyle.FixedSingle;
        this.MaximizeBox = false;
        this.Icon = new Icon("C:\Users\Admin\Desktop\Code Repositories\Visual Studios\Widget\Widget\Properties\WidgetIcon.ico");
        Pen = new Pen(Color.Black, 1);
        G = CreateGraphics();
        //this.Paint += new PaintEventHandler(OnPaint);
        ConstructMouse();
        FormWithTimer();
        DoubleBuffered = true;

        Circles = new List<Circle>();
    }

    public void ConstructMouse()
    {
        this.MouseUp += new MouseEventHandler(OnMouseUp);
        this.MouseMove += new MouseEventHandler(OnMouseMove);
        this.MouseDown += new MouseEventHandler(OnMouseDown);
    }

    public void FormWithTimer()
    {
        timer.Tick += new EventHandler(timer_Tick);
        timer.Interval = (10);
        timer.Enabled = true;
        timer.Start();
    }

    protected void OnMouseUp(object sender, MouseEventArgs e)
    {
    }

    protected void OnMouseMove(object sender, MouseEventArgs e)
    {
    }

    public void OnMouseDown(object sender, MouseEventArgs e)
    {
        Circles.Add(new Circle(e.Location.X, e.Location.Y, 0, 255));
    }

   /*public void OnPaint(object sender, PaintEventArgs e)
    {
        e.Graphics.Clear(Color.White);
        for (int i = 0; i < Circles.Count; i++)
        {
            Circle C = Circles[i];
            e.Graphics.DrawEllipse(new Pen(Color.FromArgb(C.Alpha, 0, 0, 0), 1), C.X - C.Radius, C.Y - C.Radius, 2 * C.Radius, 2 * C.Radius);
        }
    }*/

    private void Tick()
    {
        for (int i = 0; i < Circles.Count; i++)
        {
            Circle C = Circles[i];
            C.Radius++;
            C.Alpha -= 3;
            if (C.Alpha == 0)
            {
                Circles.RemoveAt(i);
            }
        }
    }

    public void Render()
    {
        G.Clear(Color.White);
        for (int i = 0; i < Circles.Count; i++)
        {
            Circle C = Circles[i];
            G.DrawEllipse(new Pen(Color.FromArgb(C.Alpha, 0, 0, 0), 1), C.X - C.Radius, C.Y - C.Radius, 2 * C.Radius, 2 * C.Radius);
        }
    }

    public void timer_Tick(object sender, EventArgs e)
    {
        Render();
        Tick();
    }
}
}

此处有更多观察,但可能就是答案。

  • 为什么使用定时器?
  • 使用 Paint 事件,当 GDI+ 确定它需要时调用它,您不断地用您的代码绘画 as-is。
  • 您的代码使您看起来好像没有使用双缓冲。

简答 - 保留 DoubleBuffered = true 并使用 Paint 事件。

When I tried to use a PaintEvent for my graphics functionality, nothing happened

当你做了一些修改并想反映它们时,使用 Control.Invalidate 方法,根据文档

Invalidates the entire surface of the control and causes the control to be redrawn.

在你的情况下,是这样的

void timer_Tick(object sender, EventArgs e)
{
    Tick();
    Invalidate();
}

我会将您的所有绘图绘制到一个单独的 Graphics 对象,然后仅在发生更改时才在计时器滴答事件上将其复制到您的主 Graphics 对象。您可能需要使用布尔成员来跟踪它。这意味着您的背景绘制必须由其他事件触发。

如果您的图片实际上每 10 毫秒更改一次,我会稍微减慢计时器并将其设置为 50 毫秒。

试试这个(在 VB 中):

Dim aProp = GetType(Control).GetProperty("DoubleBuffered", System.Reflection.BindingFlags.NonPublic Or System.Reflection.BindingFlags.Instance)
aProp.SetValue(Me, True, Nothing)

双缓冲也有问题,通常将 属性 DoubleBuffered 设置为 true 不起作用。我在网上的某个地方找到了这个解决方案