Reputation: 2409
I'm going to post my code first since it is short and easy to understand, then i'll ask my question.
public class BatteryLabel : Control
{
private Color _captionColor = SystemColors.Control;
private Color _textColor = SystemColors.Info;
private Color _failColor = Color.Red;
private Color _passColor = Color.Green;
private string _caption;
string text2;
string text3;
bool battery1Fail = false;
bool battery2Fail = false;
bool battery3Fail = false;
public BatteryLabel()
{
}
public Color BackgroundTextColor
{
get{ return _textColor;}
set{_textColor = value; Invalidate();}
}
public string Caption
{
get
{
return _caption;
}
set
{
_caption = value;
Invalidate();
}
}
public override string Text
{
get
{
return base.Text;
}
set
{
base.Text = value;
Invalidate();
}
}
public string Text2
{
get { return text2; }
set { text2 = value; Invalidate(); }
}
public string Text3
{
get { return text3; }
set { text3 = value; Invalidate(); }
}
public bool Battery1Fail
{
get { return battery1Fail; }
set { battery1Fail = value; Invalidate(); }
}
public bool Battery2Fail
{
get { return battery2Fail; }
set { battery2Fail = value; Invalidate(); }
}
public bool Battery3Fail
{
get { return battery3Fail; }
set { battery3Fail = value; Invalidate(); }
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.DrawRectangle(Pens.Black, 0,0, Width-1, Height-1);
var x1 = 50;
var x2 = 98;
var x3 = 146;
var color1 = battery1Fail?_failColor:BackgroundTextColor;
var color2 = battery2Fail?_failColor:BackgroundTextColor;
var color3 = battery3Fail?_failColor:BackgroundTextColor;
e.Graphics.FillRectangle(new SolidBrush(color1),x1+1, 1, 47, Height-2);
e.Graphics.FillRectangle(new SolidBrush(color2),x2+1, 1, 47, Height-2);
e.Graphics.FillRectangle(new SolidBrush(color3),x3+1, 1, 47, Height-2);
e.Graphics.DrawLine(Pens.Black, x1,0, x1, Height-1);
e.Graphics.DrawLine(Pens.Black, x2,0, x2, Height-1);
e.Graphics.DrawLine(Pens.Black, x3,0, x3, Height-1);
var BoldFont = new Font(this.Font, FontStyle.Bold);
e.Graphics.DrawString(Caption, BoldFont, new SolidBrush(ForeColor), 0,0);
e.Graphics.DrawString(Text, this.Font, new SolidBrush(ForeColor), x1,0);
e.Graphics.DrawString(Text2, this.Font, new SolidBrush(ForeColor), x2,0);
e.Graphics.DrawString(Text3, this.Font, new SolidBrush(ForeColor), x3,0);
}
}
The controls size is meant to be 195,14 just in case you decide to try to use it. I have 8 of these in a panel that is 200,200 running on a 1.6Ghz atom processor. It is used to display values from up to 3 batteries on a computer. The labels get refreshed every 500ms. As you may have gathered there is a little bit of flickering, but it is tolerable. I'd just like to have even less if possible. So I started looking into using Update, and moving some of my code around such as the background bit I thought maybe i should move that to OnPaintBackground(), but in a test frame that i made up the Update method does not change anything, and when I use Invalidate method it runs both OnPaintBackground and OnPaint. Here is what I tried in that case.
public class InformationLabel : Control
{
Random r = new Random();
protected override void OnPaintBackground(PaintEventArgs e)
{
base.OnPaintBackground(e);
Color randomCOlor = Color.FromArgb(r.Next());
e.Graphics.FillRectangle(new SolidBrush(randomCOlor),0,0, Width-1, Height-1);
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Color randomCOlor = Color.FromArgb(r.Next());
e.Graphics.FillPie(new SolidBrush(randomCOlor),15,15,15,15, 0.0f, 120.0f);
}
}
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
void Button1Click(object sender, EventArgs e)
{
informationLabel1.Update();
}
void Button2Click(object sender, EventArgs e)
{
informationLabel1.Invalidate();
}
}
I made that one usercontrol about 300,300 so i could be sure of what i was seeing. I forgot to mention that in the battery control in my 500ms timer i just renew text, text2, and text3. I'm thinking that if the value of that text is out of spec that I'll set the battery fail flag and then invalidate.. but i'm not sure. So how should I go about updating only the text???
Upvotes: 0
Views: 247
Reputation: 14517
You can get rid of the flickering by adding this line in your constructor:
SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint | ControlStyles.Opaque | ControlStyles.AllPaintingInWmPaint, true);
Now paint both background and everything else in your paint handler.
Optimizing can be done by passing a Rectangle to Invalidate for only the area that needs repainting. Then in your OnPaint override you use e.ClipRectangle to figure out what to draw. This is probably not necessary for such a simple drawing.
Upvotes: 3
Reputation: 2901
I believe you are looking in the wrong place to eliminate flicker. I can use your BatteryLabel
to get flickerless updating of the text with basically a single line. Change your constructor to look like this:
public BatteryLabel()
{
this.SetStyle(ControlStyles.OptimizedDoubleBuffer,true);
}
This tells the control to double-buffer its graphics, which makes the flicker go away.
To test with a 100ms refresh interval:
Timer t;
public Form1()
{
InitializeComponent();
t = new Timer();
t.Interval = 100;
t.Tick += new EventHandler(t_Tick);
t.Start();
}
void t_Tick(object sender, EventArgs e)
{
string ticks = DateTime.Now.Ticks.ToString();
string ticks1 = ticks.Substring(ticks.Length-4),
ticks2 = ticks.Substring(ticks.Length - 5,4),
ticks3 = ticks.Substring(ticks.Length - 6,4);
batteryLabel1.Text = ticks1;
batteryLabel1.Text2 = ticks2;
batteryLabel1.Text3 = ticks3;
batteryLabel1.Battery1Fail = ticks1.StartsWith("1");
batteryLabel1.Battery2Fail = ticks2.StartsWith("1");
batteryLabel1.Battery3Fail = ticks3.StartsWith("1");
}
Does this help, or have I misunderstood you?
Upvotes: 2