Графика Winforms мерцает. (Двойная буферизация не помогает!)

Я пытаюсь создать простое графическое приложение Windows Forms, которое в основном будет рисовать круг каждый раз, когда пользователь щелкает, и он расширяется, медленно исчезая.

Когда я попытался использовать событие Paint() для своих графических функций, ничего не произошло, поэтому я создал отдельную функцию под названием «Рендеринг», которая вызывается в моем основном обновлении 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();
    }
}
}

person Adraria    schedule 30.12.2015    source источник
comment
Где вы добавили двойную буферизацию?   -  person Phiter    schedule 31.12.2015
comment
почему вы чувствуете необходимость использовать таймер? таймер на 10 мс... вот почему вы мерцаете.   -  person T McKeown    schedule 31.12.2015
comment
Филтер Фернадес: В конструкторе. T McUnknown: Как мне это сделать?   -  person Adraria    schedule 31.12.2015
comment
DoubleBuffered не работает на G = CreateGraphics();. Вы должны использовать событие рисования и каждый раз перерисовывать все.   -  person LarsTech    schedule 31.12.2015
comment
Если вы довольны ответом, рассмотрите возможность его принятия..!   -  person TaW    schedule 31.12.2015


Ответы (4)


Короткий ответ - сохранить DoubleBuffered = true и использовать событие Paint.

Когда я попытался использовать PaintEvent для своих графических функций, ничего не произошло.

Когда вы вносите некоторые изменения и хотите их отразить, используйте Control.Invalidate, который согласно документации

Делает недействительной всю поверхность элемента управления и вызывает перерисовку элемента управления.

В вашем случае как-то так

void timer_Tick(object sender, EventArgs e)
{
    Tick();
    Invalidate();
}
person Ivan Stoev    schedule 30.12.2015

Больше наблюдений здесь, но, вероятно, будет ответом.

  • Почему Таймер?
  • Используйте событие Paint, оно вызывается, когда GDI+ определяет, что это необходимо, вы постоянно рисуете своим кодом как есть.
  • Ваш код выглядит так, будто вы не используете двойную буферизацию.
person T McKeown    schedule 30.12.2015
comment
Спасибо за ответ! 1. Первоначально я использовал таймер для увеличения кругов каждые 10 мс, но включил рендеринг, потому что событие рисования не было рисованием. 2. Когда я использую событие Paint, ничего не происходит... Это потому, что я неправильно выполняю двойную буферизацию или что-то еще? 3. В конструкторе я установил для свойства DoubleBuffered значение true. Есть ли что-то еще, что мне нужно сделать? Извините, я не знаю, я новичок. :) - person Adraria; 31.12.2015

Я бы сделал весь ваш рисунок в отдельный объект Graphics, а затем скопировал бы его в ваш основной объект Graphics в событии таймера, только если произошли изменения. Возможно, вам придется отслеживать это с помощью логического члена. Это означает, что ваш фоновый рисунок должен быть вызван каким-то другим событием.

Если ваше изображение на самом деле меняется каждые 10 миллисекунд, я бы немного замедлил таймер и установил его на 50 миллисекунд.

person Tim    schedule 31.12.2015

Попробуйте это (в VB):

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

Также были проблемы с двойной буферизацией, и обычная установка свойства DoubleBuffered в true не работает. Я нашел это решение где-то в Интернете

person codefox    schedule 31.12.2015