Подтвердить что ты не робот

Как остановить мерцание С# winforms

У меня есть программа, которая по существу похожа на приложение для рисования. Однако у моей программы есть некоторые проблески. У меня есть следующая строка в моем коде (которая должна избавиться от мерцания - но не):

this.SetStyle(ControlStyles.AllPaintingInWmPaint 
| ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true);

мой код (минус супер и подкласс для фигур выглядит следующим образом:

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

namespace Paint
{
    public partial class Paint : Form
    {
        private Point startPoint;
        private Point endPoint;
        private Rectangle rect = new Rectangle();
        private Int32 brushThickness = 0;
        private Boolean drawSPaint = false;
        private List<Shapes> listOfShapes = new List<Shapes>();
        private Color currentColor;
        private Color currentBoarderColor;
        private Boolean IsShapeRectangle = false;
        private Boolean IsShapeCircle = false;
        private Boolean IsShapeLine = false;

        public SPaint()
        {

            InitializeComponent();
            this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true);

            currentColor = Color.Red;
            currentBoarderColor = Color.DodgerBlue;
            IsShapeRectangle = true; 
        }

        private void panelArea_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = panelArea.CreateGraphics();

            if (drawSPaint == true)
            {

                Pen p = new Pen(Color.Blue);
                p.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;

                if (IsShapeRectangle == true)
                {
                    g.DrawRectangle(p, rect);
                }
                else if (IsShapeCircle == true)
                {
                    g.DrawEllipse(p, rect);
                }
                else if (IsShapeLine == true)
                {
                    g.DrawLine(p, startPoint, endPoint);
                }
            }
            foreach (Shapes shape in listOfShapes)
            {

                shape.Draw(g);

            }
        }

        private void panelArea_MouseDown(object sender, MouseEventArgs e)
        {

            startPoint.X = e.X;
            startPoint.Y = e.Y;

            drawSPaint = true;
        }

        private void panelArea_MouseMove(object sender, MouseEventArgs e)
        {


            if (e.Button == System.Windows.Forms.MouseButtons.Left)
            {

                if (e.X > startPoint.X)
                {
                    rect.X = startPoint.X;
                    rect.Width = e.X - startPoint.X;
                }
                else
                {
                    rect.X = e.X;
                    rect.Width = startPoint.X - e.X;
                }
                if (e.Y > startPoint.Y)
                {
                    rect.Y = startPoint.Y;
                    rect.Height = e.Y - startPoint.Y;
                }
                else
                {
                    rect.Y = e.Y;
                    rect.Height = startPoint.Y - e.Y;
                }


                panelArea.Invalidate();

            }

        }

        private void panelArea_MouseUp(object sender, MouseEventArgs e)
        {

            endPoint.X = e.X;
            endPoint.Y = e.Y;

            drawSPaint = false;

            if (rect.Width > 0 && rect.Height > 0)
            {
                if (IsShapeRectangle == true)
                {
                    listOfShapes.Add(new TheRectangles(rect, currentColor, currentBoarderColor, brushThickness));
                }
                else if (IsShapeCircle == true)
                {
                    listOfShapes.Add(new TheCircles(rect, currentColor, currentBoarderColor, brushThickness));
                }
                else if (IsShapeLine == true)
                {
                    listOfShapes.Add(new TheLines(startPoint, endPoint, currentColor, currentBoarderColor, brushThickness));
                }

                panelArea.Invalidate();
            }
        }


        private void rectangleToolStripMenuItem_Click(object sender, EventArgs e)
        {
            IsShapeRectangle = true;
            IsShapeCircle = false;
            IsShapeLine = false; 
        }

        private void ellipseToolStripMenuItem_Click(object sender, EventArgs e)
        {
            IsShapeRectangle = false;
            IsShapeCircle = true;
            IsShapeLine = false; 
        }

        private void lineToolStripMenuItem_Click(object sender, EventArgs e)
        {
            IsShapeCircle = false;
            IsShapeRectangle = false;
            IsShapeLine = true; 
        }

        private void ThicknessLevel0_Click(object sender, EventArgs e)
        {
            brushThickness = 0; 
        }

        private void ThicknessLevel2_Click(object sender, EventArgs e)
        {
            brushThickness = 2; 
        }

        private void ThicknessLevel4_Click(object sender, EventArgs e)
        {
            brushThickness = 4; 
        }

        private void ThicknessLevel6_Click(object sender, EventArgs e)
        {
            brushThickness = 6; 
        }

        private void ThicknessLevel8_Click(object sender, EventArgs e)
        {
            brushThickness = 8; 
        }

        private void ThicknessLevel10_Click(object sender, EventArgs e)
        {
            brushThickness = 10; 
        }

        private void ThicknessLevel12_Click(object sender, EventArgs e)
        {
            brushThickness = 12; 
        }

        private void ThicknessLevel14_Click(object sender, EventArgs e)
        {
            brushThickness = 14; 
        }

        private void FillColour_Click(object sender, EventArgs e)
        {

            ColorDialog fillColourDialog = new ColorDialog();
            fillColourDialog.ShowDialog();
            currentColor = fillColourDialog.Color;
            panelArea.Invalidate(); 
        }

        private void button1_Click(object sender, EventArgs e)
        {

            ColorDialog fillColourDialog = new ColorDialog();
            fillColourDialog.ShowDialog();
            currentBoarderColor = fillColourDialog.Color;
            panelArea.Invalidate(); 
        }


    }
}

Как остановить мерцание?

* ОБНОВЛЕНИЕ: * Этот код действительно отлично работает, когда я рисую непосредственно на форме. Однако, когда я пытаюсь нарисовать панель, мерцание становится проблемой

4b9b3361

Ответ 1

Наконец решил мерцание. Поскольку я рисовал панель вместо формы, строка кода ниже не решит мерцание:

this.SetStyle(
    ControlStyles.AllPaintingInWmPaint | 
    ControlStyles.UserPaint | 
    ControlStyles.DoubleBuffer, 
    true);

SetStyle должен быть типа "YourProject.YourProject" (или получен из него), следовательно, вы должны создать класс как таковой (чтобы вы могли использовать MyPanel, который будет получен из SPaint.SPaint и, следовательно, позволит вам использовать двойное буферирование непосредственно для панели - а не форма):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SPaint; 

namespace YourProject
{
    public class MyPanel : System.Windows.Forms.Panel
    {
        public MyPanel()
        {
            this.SetStyle(
                System.Windows.Forms.ControlStyles.UserPaint | 
                System.Windows.Forms.ControlStyles.AllPaintingInWmPaint | 
                System.Windows.Forms.ControlStyles.OptimizedDoubleBuffer, 
                true);
        }
    }
}

После того, как вы это сделали (хотя вы действительно никогда не будете редактировать код дизайнера, если вы действительно не знаете, что делаете), вам нужно будет отредактировать форму .Designer.cs. Внутри этого файла вы найдете код, который выглядит так:

this.panelArea = new YourProject.MyPanel();

Вышеупомянутая строка должна быть изменена на:

this.panelArea = new MyPanel(); 

После выполнения этих шагов моя программа рисования больше не мерцает.

Для всех, кто имеет одну и ту же проблему, проблема окончательно решена.

Наслаждайтесь!

Ответ 2

Для "более чистого решения" и для того, чтобы продолжать использовать базовую панель, вы можете просто использовать Reflection для реализации двойной буферизации, добавив этот код в форму, содержащую панели, в которые вы хотите нарисовать

    typeof(Panel).InvokeMember("DoubleBuffered", 
    BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.NonPublic, 
    null, DrawingPanel, new object[] { true });

Где "DrawingPanel" - это имя панели, для которой вы хотите выполнить двойную буферизацию.

Я знаю, что прошло много времени с тех пор, как был задан вопрос, но это может помочь кому-то в будущем.

Ответ 3

Скопируйте и вставьте это в свой проект

protected override CreateParams CreateParams
{
    get
    {
        CreateParams handleParam = base.CreateParams;
        handleParam.ExStyle |= 0x02000000;   // WS_EX_COMPOSITED       
        return handleParam;
    }
}

Это позволяет выполнять двойную буферизацию для всех элементов управления с уровня формы вниз, в то время как для каждой из них необходимо индивидуально активировать двойную буферизацию... вы можете настроить тонкую настройку двойного буферирования после этого, поскольку двойная буферная заливка может давать нежелательные побочные эффекты.

Ответ 4

У меня была та же проблема. Я никогда не мог на 100% избавиться от мерцания (см. Пункт 2), но я использовал это

protected override void OnPaint(PaintEventArgs e) {}

а также

this.DoubleBuffered = true;

Основной проблемой для мерцания является то, что вы

  • нарисуйте вещи в правильном порядке!
  • убедитесь, что ваша функция рисования равна < около 1/60 секунды

winforms вызывает метод OnPaint каждый раз, когда форму нужно перерисовать. Есть много способов, которыми это может быть отменено, в том числе перемещение курсора мыши над формой иногда может вызвать событие перерисовывания.

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

Наконец, ваш объект gfx. Внутри OnPaint вам нужно будет воссоздать графический объект, но ТОЛЬКО, если размер экрана изменился. воссоздание объекта очень дорого, и его нужно утилизировать до его воссоздания (сбор мусора не на 100% обрабатывает его правильно или так говорит документация). Я создал переменную класса

protected Graphics gfx = null;

а затем использовал его локально в OnPaint, как это было, но это было связано с тем, что мне нужно было использовать объект gfx в других местах моего класса. В противном случае НЕ ДЕЛАЙТЕ ЭТО. Если вы только рисуете в OnPaint, используйте e.Graphics!!

// clean up old graphics object
gfx.Dispose();

// recreate graphics object (dont use e.Graphics, because we need to use it 
// in other functions)
gfx = this.CreateGraphics();

Надеюсь, что это поможет.

Ответ 5

Двойная буферизация здесь не поможет. Я боюсь. Я столкнулся с этим некоторое время назад и в итоге добавил отдельную панель довольно неуклюжим способом, но это сработало для моего приложения.

Сделайте панель оригинала (панель) прозрачной областью и поместите ее поверх второй панели, которую вы вызываете, например, panelDraw. Убедитесь, что панель впереди. Я взбесил это, и он избавился от мерцания, но оставил форму, которую рисовали намазанной, так что это тоже не полное решение.

Прозрачную панель можно сделать, переопределив некоторые действия краски с исходной панели:

public class ClearPanel : Panel
{
    public ClearPanel(){}

    protected override CreateParams CreateParams
    {
        get
        {
            CreateParams createParams = base.CreateParams;
            createParams.ExStyle |= 0x00000020;
            return createParams;
        }
    }

    protected override void OnPaintBackground(PaintEventArgs e){}
}

Идея состоит в том, чтобы обрабатывать рисование временной фигуры во время события MouseMove в панели panelArea и ТОЛЬКО перерисовать "panelDraw" в MouseUp Event.

// Use the panelDraw paint event to draw shapes that are done
void panelDraw_Paint(object sender, PaintEventArgs e)
{
    Graphics g = panelDraw.CreateGraphics();

    foreach (Rectangle shape in listOfShapes)
    {
        shape.Draw(g);
    }
}

// Use the panelArea_paint event to update the new shape-dragging...
private void panelArea_Paint(object sender, PaintEventArgs e)
{
    Graphics g = panelArea.CreateGraphics();

    if (drawSETPaint == true)
    {
        Pen p = new Pen(Color.Blue);
        p.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;

        if (IsShapeRectangle == true)
        {
            g.DrawRectangle(p, rect);
        }
        else if (IsShapeCircle == true)
        {
            g.DrawEllipse(p, rect);
        }
        else if (IsShapeLine == true)
        {
            g.DrawLine(p, startPoint, endPoint);
        }
    }
}

private void panelArea_MouseUp(object sender, MouseEventArgs e)
{

    endPoint.X = e.X;
    endPoint.Y = e.Y;

    drawSETPaint = false;

    if (rect.Width > 0 && rect.Height > 0)
    {
        if (IsShapeRectangle == true)
        {
            listOfShapes.Add(new TheRectangles(rect, currentColor, currentBoarderColor, brushThickness));
        }
        else if (IsShapeCircle == true)
        {
            listOfShapes.Add(new TheCircles(rect, currentColor, currentBoarderColor, brushThickness));
        }
        else if (IsShapeLine == true)
        {
            listOfShapes.Add(new TheLines(startPoint, endPoint, currentColor, currentBoarderColor, brushThickness));
        }

        panelArea.Invalidate();
    }

    panelDraw.Invalidate();
}

Ответ 6

Я знаю, что это действительно старый вопрос, но, возможно, кто-то найдет его полезным. Я бы хотел немного улучшить ответ на гадюку.

Вы можете сделать простое расширение для класса Panel и скрыть свойство настройки путем отражения.

public static class MyExtensions {

    public static void SetDoubleBuffered(this Panel panel) {
        typeof(Panel).InvokeMember(
           "DoubleBuffered",
           BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.SetProperty,
           null,
           panel,
           new object[] { true });
    }
}

Если имя переменной Panel - myPanel, вы можете просто позвонить myPanel.SetDoubleBuffered();
и что это. Код выглядит намного чище.

Ответ 7

Я бы посоветовал переопределить OnPaintBackground и обработать фоновое стирание самостоятельно. Если вы знаете, что рисуете весь элемент управления, вы можете просто ничего не делать в OnPaintBackground (не называйте базовый метод), и это предотвратит сначала окрашивание фона

Ответ 8

В этом состоянии вам нужно включить двойной буфер. Откройте текущую форму и перейдите к свойствам формы и примените двойной буфер true; или вы также можете написать этот код.

this.DoubleBuffered = true;     

В загрузке формы.

Ответ 9

вот программа перемещения круга в .net, которая не мерцает.

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using System.Threading;
namespace CircleMove
{
    /// <summary>
    /// Description of MainForm.
    /// </summary>
    public partial class MainForm : Form
    {
        int x=0,y=0;
        Thread t;

        public MainForm()
        {

            //
            // The InitializeComponent() call is required for Windows Forms designer support.
            //
            InitializeComponent();

            //
            // TODO: Add constructor code after the InitializeComponent() call.
            //
        }
        void MainFormPaint(object sender, PaintEventArgs e)
        {
            Graphics g=e.Graphics;
            Pen p=new Pen(Color.Orange);
            Brush b=new SolidBrush(Color.Red);
        //  g.FillRectangle(b,0,0,100,100);
            g.FillEllipse(b,x,y,100,100);
        }
        void MainFormLoad(object sender, EventArgs e)
        {
            t=new Thread(  new ThreadStart(

                ()=>{
                    while(true)
                    {
                        Thread.Sleep(10);
                        x++;y++;
                        this.Invoke(new Action(
                            ()=>{

                                this.Refresh();
                                this.Invalidate();
                                this.DoubleBuffered=true;
                                }
                                            )
                                        );
                    }
                    }
                                            )

                        );

            t.Start();
        }
    }
}

Ответ 10

Если память плотная (так что вы не хотите, чтобы стоимость памяти была двойной буферизацией), один из возможных способов REDUCE, хотя и не устранить, мерцание, заключается в том, чтобы установить цвет фона для доминирующего цвета в вашей текущей сцене.

Почему это помогает: мерцание - это мгновенная вспышка цвета фона, которую ОС рисует перед рисованием дочерних элементов управления или вашего пользовательского кода чертежа. Если эта вспышка является цветом, который ближе к окончательному цвету, который будет отображаться, он будет менее заметным.

Если вы не знаете, с какого цвета начать, начните с 50% серого цвета, потому что это средний черно-белый цвет, поэтому он будет ближе к большинству цветов в вашей сцене.

myFormOrControl.BackColor = Color.Gray;

Ответ 11

Попробуйте вставить схему рисования в текущей форме

protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint(e);
}

метод. В этом случае вы должны использовать параметр e для получения объекта Graphics. Используйте свойство e.Graphics. Затем вы должны вызывать метод Invalidate() для этой формы всякий раз, когда форма должна быть перерисована. PS: DoubleBuffered должен иметь значение true.

Ответ 13

просто сделайте это. this.Refresh() когда отображается форма.

Ответ 14

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

Или просто проверьте, не нажата ли мышь (через boolean, которая устанавливает true, когда мышь), используя таймер и нарисуйте его, учитывая, что вы, вероятно, пытаетесь просто нарисовать один пиксель, а не как тень и т.д. Вместо использования фактического mousedown. Таким образом, вы проверяете каждую секунду вместо 0.0001, и это не мерцает. Или наоборот, попробуйте в свое время.