如何停止闪烁C#winforms

Big*_*Bug 65 c# drawing

我有一个基本上像绘图应用程序的程序.但是,我的程序有一些闪烁的问题.我的代码中有以下行(它应该摆脱闪烁 - 但不是):

this.SetStyle(ControlStyles.AllPaintingInWmPaint 
| ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true);
Run Code Online (Sandbox Code Playgroud)

我的代码(减去形状的超类和子类如下:

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(); 
        }


    }
}
Run Code Online (Sandbox Code Playgroud)

我该如何阻止闪烁?

*更新:*当我直接在表单上绘图时,这段代码实际上很有效.但是,当我尝试在面板上绘图时,闪烁成为一个问题

vip*_*per 60

对于"更干净的解决方案"并且为了继续使用基本面板,您可以简单地使用Reflection来实现双缓冲,方法是将此代码添加到包含要在其中绘制的面板的表单中

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

其中"DrawingPanel"是您要进行双缓冲的面板的名称.

自从问到这个问题以来,我知道已经过了很多时间,但这可能会对未来的某些人有所帮助.

  • @musefan我很高兴能帮助你:D我很高兴比原始问题晚2年发布的解决方案仍能帮助人们!这就是堆栈溢出非常好的原因!:) (5认同)
  • 这是一个很好的解决方案!实现起来非常简单,并且可以完全按照需要工 (3认同)
  • @RotaryHeart请记住,你不应该在paint方法中使用它,因为它会在每次重新绘制控件时运行,并且反射很昂贵.您只需要为每个控件设置一次.我不确定这是否是你所做的,从阅读你的评论看起来它可能是你的解决方案. (3认同)
  • 在开始使用闪烁之前,您必须为要停止闪烁的面板调用此方法.您可以在主窗体的Load事件(或具有面板的窗体)中调用它.你在哪里叫它? (2认同)

Big*_*Bug 53

终于解决了闪烁问题.由于我在面板而不是表单上绘图,下面的代码行无法解决闪烁问题:

this.SetStyle(
    ControlStyles.AllPaintingInWmPaint | 
    ControlStyles.UserPaint | 
    ControlStyles.DoubleBuffer, 
    true);
Run Code Online (Sandbox Code Playgroud)

SetStyle必须是'YourProject.YourProject'类型(或从它派生),因此,你必须创建一个类(这样你就可以使用MyPanel,它将从SPaint.SPaint派生,因此允许你直接使用doublebuffering小组 - 而不是形式):

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

完成此操作后(尽管您真的不应该编辑设计器代码,除非您真正知道自己在做什么),否则您必须编辑Form.Designer.cs.在此文件中,您将找到如下所示的代码:

this.panelArea = new YourProject.MyPanel();
Run Code Online (Sandbox Code Playgroud)

上述行需要更改为:

this.panelArea = new MyPanel(); 
Run Code Online (Sandbox Code Playgroud)

完成这些步骤后,我的绘画程序不再闪烁.

对于其他任何有同样问题的人来说,问题终于解决了.

请享用!


小智 28

将其复制并粘贴到您的项目中

protected override CreateParams CreateParams
{
    get
    {
        CreateParams handleParam = base.CreateParams;
        handleParam.ExStyle |= 0x02000000;   // WS_EX_COMPOSITED       
        return handleParam;
    }
}
Run Code Online (Sandbox Code Playgroud)

这样就可以从表单级别向所有控件进行双缓冲,否则需要为每个控件单独启用双缓冲...您可能需要在此之后微调双缓冲区,因为覆盖双缓冲可能会产生不必要的副作用.

  • 为了使您的答案更有用,请考虑添加一个简短的解释,说明为什么特定的代码可能有助于回答问题. (8认同)
  • 谢谢你的解决方案.它为什么有效? (2认同)
  • 它看起来像[这可能是来源](http://www.angryhacker.com/blog/archive/2010/07/21/how-to-get-rid-of-flicker-on-windows-forms-applications .aspx),也发布了[这里的SO](http://stackoverflow.com/questions/3286373/flickering-in-a-windows-forms-app)...为什么这个工作的基本解释是因为它从表单级别开始为所有控件启用双缓冲,否则需要为每个控件单独启用双缓冲...您可能需要在此之后微调双缓冲区,因为覆盖双缓冲可能会产生不必要的副作用 (2认同)

ohm*_*ama 15

我曾经也有过一样的问题.我永远无法100%摆脱闪烁(见第2点),但我用过这个

protected override void OnPaint(PaintEventArgs e) {}
Run Code Online (Sandbox Code Playgroud)

以及

this.DoubleBuffered = true;
Run Code Online (Sandbox Code Playgroud)

闪烁的主要问题是确保你

  1. 把它做成正确的订单!
  2. 确保你的绘图功能<大约1/60秒

OnPaint每次需要重绘表单时,winforms都会调用该方法.有许多方法可以取消验证,包括在表单上移动鼠标光标有时可以调用重绘事件.

而且重要的一点是OnPaint,你是不是每次都从头开始,而是从你所在的地方开始,如果你填充背景颜色,你可能会变得闪烁.

最后你的gfx对象.在内部,OnPaint您将需要重新创建图形对象,但仅限于屏幕大小已更改.重新创建对象是非常昂贵的,它需要在重新创建之前进行处理(垃圾收集不能100%正确处理它,所以说文档).我创建了一个类变量

protected Graphics gfx = null;
Run Code Online (Sandbox Code Playgroud)

然后在本地使用它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();
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助.