I have the code below which is an implementation of a marquee label. I'd like the marquee label to run at the most efficient rate possible on the computer while maintaining a constant speed across different computers. I'd like the amount of "frames" that are skipped to be minimized so that the program takes full advantage of the computer it's being run on. But at the same time I don't necessarily want to consume 100% of the CPU (are those statements contradictory? I'm not sure).
Currently I'm sleeping 10 milliseconds for each iteration of the animation loop. This feels wasteful to me and it seems like it might slow down the animation on slower computers that might need those extra 10 milliseconds. I'm not sure what the best value is to use in the sleep method or even if I should sleep at all. I've read some about Sleep(0) and Sleep(1) and Yield and SpinWait, but I can't make sense of it all.
Also is it a bad thing to call Invalidate too much? Can I overload it by calling it too much?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using System.ComponentModel;
using System.Diagnostics;
using Timer = System.Timers.Timer;
using System.Threading;
namespace Scoreboard
{
public class MarqueeLabel : Label
{
private float _marqueeOffset;
private float _marqueeMeasurement;
public MarqueeLabel()
{
UseCompatibleTextRendering = true;
this.SetStyle(ControlStyles.DoubleBuffer, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.UserPaint, true);
Thread t = new Thread(ThreadRun);
t.IsBackground = true;
t.Start();
}
public void ThreadRun()
{
long time = DateTime.Now.Ticks;
long diff = 0;
while (true)
{
float step = -((float)diff / (20 * TimeSpan.TicksPerMillisecond)); //change "frame" based on the elapsed time
_marqueeOffset = _marqueeOffset >= -_marqueeMeasurement ? _marqueeOffset + step : Width;
Invalidate();
Thread.Sleep(10); // how long should i wait here?
long temp = DateTime.Now.Ticks;
diff = temp - time;
time = temp;
}
}
protected override void OnPaint(PaintEventArgs e)
{
StringFormat stringFormat = new StringFormat();
stringFormat.FormatFlags = StringFormatFlags.NoClip | StringFormatFlags.NoWrap;
stringFormat.Trimming = StringTrimming.None;
stringFormat.Alignment = StringAlignment.Near;
Rectangle rect = ClientRectangle;
rect.X += (int)_marqueeOffset;
e.Graphics.DrawString(Text, Font, new SolidBrush(ForeColor), rect, stringFormat);
}
protected override void OnTextChanged(EventArgs e)
{
base.OnTextChanged(e);
MeasureText();
}
protected override void OnFontChanged(EventArgs e)
{
base.OnFontChanged(e);
MeasureText();
}
void MeasureText()
{
_marqueeMeasurement = CreateGraphics().MeasureString(Text, Font).Width;
}
}
}