views:

109

answers:

7

Hi

I have a problem with the Label control that is terribly flickering.

Below is some code to reproduce the problem.

How to solve this?

UPDATE: The solution for the previous case (a Form contains a Label directly) was to make the form.DoubleBuffered = true. But this is not a generic solution. For example, what should I do in the case of label inside a SplitContainer? this is my real case.

UPDATED CODE:

DoubleBufferedLabel.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace FlickerLabelTest
{
    public class DoubleBufferedLabel : Label
    {
        public DoubleBufferedLabel()
        {
            DoubleBuffered = true;
        }
    }
}

DoubleBufferedSplitContainer.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace FlickerLabelTest
{
    public class DoubleBufferedSplitContainer : SplitContainer
    {
        public DoubleBufferedSplitContainer()
        {
            DoubleBuffered = true;
        }
    }
}

Form1.cs:

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 FlickerLabelTest
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            label1.Text += "0";
        }
    }
}

Form1.Designer.cs:

namespace FlickerLabelTest
{
    partial class Form1
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();
            this.timer1 = new System.Windows.Forms.Timer(this.components);
            this.label1 = new FlickerLabelTest.DoubleBufferedLabel();
            this.splitContainer1 = new DoubleBufferedSplitContainer();
            this.splitContainer1.Panel2.SuspendLayout();
            this.splitContainer1.SuspendLayout();
            this.SuspendLayout();
            // 
            // timer1
            // 
            this.timer1.Enabled = true;
            this.timer1.Interval = 1;
            this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
            // 
            // label1
            // 
            this.label1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.label1.Location = new System.Drawing.Point(0, 0);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(186, 262);
            this.label1.TabIndex = 0;
            this.label1.Text = "label1";
            // 
            // splitContainer1
            // 
            this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.splitContainer1.Location = new System.Drawing.Point(0, 0);
            this.splitContainer1.Name = "splitContainer1";
            // 
            // splitContainer1.Panel2
            // 
            this.splitContainer1.Panel2.Controls.Add(this.label1);
            this.splitContainer1.Size = new System.Drawing.Size(284, 262);
            this.splitContainer1.SplitterDistance = 94;
            this.splitContainer1.TabIndex = 1;
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(284, 262);
            this.Controls.Add(this.splitContainer1);
            this.DoubleBuffered = true;
            this.Name = "Form1";
            this.Text = "Form1";
            this.WindowState = System.Windows.Forms.FormWindowState.Maximized;
            this.splitContainer1.Panel2.ResumeLayout(false);
            this.splitContainer1.ResumeLayout(false);
            this.ResumeLayout(false);

        }

        #endregion

        private System.Windows.Forms.Timer timer1;
        private DoubleBufferedLabel label1;
        private DoubleBufferedSplitContainer splitContainer1;
    }
}

Program.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;

namespace FlickerLabelTest
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }
    }
}
+1  A: 

Not really an answer, but why would you want to update your label each millisecond? If you meant 1 second, you'd have to set your interval to 1000.

You can solve this by giving the form time to redraw itself and using a larger interval.

Update: turned out, setting DoubleBuffered to true solves the problem. Thanks for csharptest.net for pointing this out and DxCK for correcting me.

gaearon
Flickering is not about the timer interval, its about the paint implementation. The label flickering even if I set the timer to interval 1000, and its annoying my eyes. 1ms is for the fast reproduce. A normal double-buffered control will not flicker even in 1ms update rate.
DxCK
You're right, my mistake. I forgot about DoubleBuffered being false by default.
gaearon
A: 

Stop setting timers to 1ms. No seriously, what you see here is that the Label tries to keep up with it's changes, but fails to do so because they're to frequent. So possible solutions are:

  • Choose a way to change the Label less often
  • Activate Double-Buffering on the Form
Bobby
+2  A: 

I think your looking for this: http://msdn.microsoft.com/en-us/library/3t7htc9c.aspx

example:

class Form1 : Form
{
   public Form1() {
      this.DoubleBuffered = true;
   }
}
csharptest.net
+1  A: 

Paste this into your form code to help the Dock layout calculations:

    protected override void OnLoad(EventArgs e) {
        label1.Size = this.ClientSize;
        base.OnLoad(e);
    }
Hans Passant
So far your solution is the best, because it generic, it working when Label directly inside a Form, and even inside a SplitContainer. Can you please explain why it solving the flickering? How it working?
DxCK
I could but then the answer might accidentally become helpful. It looks like you ought to ask the guy with the two helpful votes.
Hans Passant
A: 

Hi,

Why not run your label update function via an asynchronous delegate? Or use System.Threading namespace for a different flavor.

Also, as people before me mentioned, it would be useful if you set the DoubleBuffer property on your form to true (it's no silver bullet though).

Jas
A: 

Hi,

Activating Double Buffering on the form will fix the problem. But that is actually an expensive solution. If you just add the following to the form:

 SetStyle(ControlStyles.AllPaintingInWmPaint, true);

the flicker will be over too.

The default behavior of the Windows OS it to first let all windows paint their background, and later, let them do actual painting. This is from the old days, when painting letters actually took considerable amount of time. This flag tells it to fold the background paint and the regular paint (for the same window) immediately after each other.

Real Double buffering can be reserved for the cases where you actually do painting yourself (when you override OnPaint).

jdv
+2  A: 

The problem is with the docking. If you change the Label.Dock from Fill to None, manually set the size of the label to fill the split panel and then anchor it on all sides, it won't flicker.

If you want to see the cause of the flicker, while Dock is still set to Fill, override OnResize in your DoubleBufferedLabel class, start the application, and while it is running set a breakpoint in OnResize. Add a watch for the Size and you'll see it flipping between its design time and runtime sizes.

I tried using a regular SplitContainer and Label in your example, set DoubleBuffer to False on the form, and it did not flicker if I left Dock set to None on the Label.

adrift