2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.Diagnostics;
7 using System.Threading.Tasks;
9 using System.Windows.Forms;
12 namespace SharpDisplayManager
14 [System.ComponentModel.DesignerCategory("Code")]
15 public class MarqueeLabel : Label
17 private bool iOwnTimer;
18 private StringFormat iStringFormat;
19 private SolidBrush iBrush;
20 private SizeF iTextSize;
21 private SizeF iSeparatorSize;
22 private SizeF iScrollSize;
23 private Font iFontInUse;
25 [Category("Appearance")]
26 [Description("Separator in our scrolling loop.")]
28 [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
29 [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
30 public string Separator { get; set; }
32 [Category("Behavior")]
33 [Description("How fast is our text scrolling, in pixels per second.")]
35 [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
36 [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
37 public int PixelsPerSecond { get; set; }
39 [Category("Behavior")]
40 [Description("Should we scale down our font to try fit our text without scrolling.")]
42 [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
43 [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
44 public bool ScaleToFit { get; set; }
46 [Category("Behavior")]
47 [Description("Minimum size of our font allowed when scaling is enabled.")]
49 [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
50 [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
51 public float MinFontSize { get; set; }
53 [Category("Behavior")]
54 [Description("Use an internal or an external timer.")]
56 [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
57 [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
72 Timer.Tick += new EventHandler(Timer_Tick);
85 private int CurrentPosition { get; set; }
86 private Timer Timer { get; set; }
87 private DateTime LastTickTime { get; set; }
88 private double PixelsLeft { get; set; }
89 //DateTime a = new DateTime(2010, 05, 12, 13, 15, 00);
90 //DateTime b = new DateTime(2010, 05, 12, 13, 45, 00);
91 //Console.WriteLine(b.Subtract(a).TotalMinutes);
95 UseCompatibleTextRendering = true;
96 //PixelsPerSecond = 32;
97 LastTickTime = DateTime.Now;
100 iBrush = new SolidBrush(ForeColor);
103 //Just clone our font
104 iFontInUse = new Font(Font, Font.Style);
106 //Following is needed if we ever switch from Label to Control base class.
107 //Without it you get some pretty nasty flicker
108 //SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
109 //SetStyle(ControlStyles.UserPaint, true);
110 //SetStyle(ControlStyles.AllPaintingInWmPaint, true);
111 //SetStyle(ControlStyles.DoubleBuffer, true);
114 public void UpdateAnimation(DateTime aLastTickTime, DateTime aNewTickTime)
123 while (CurrentPosition > (iTextSize.Width + iSeparatorSize.Width))
125 CurrentPosition -= ((int)(iTextSize.Width + iSeparatorSize.Width));
129 while (CurrentPosition > iScrollSize.Width)
131 CurrentPosition -= (int)iScrollSize.Width;
135 PixelsLeft += aNewTickTime.Subtract(aLastTickTime).TotalSeconds * PixelsPerSecond;
137 //Keep track of our pixels left over
138 //PixelsLeft = offset - Math.Truncate(offset);
139 double offset = Math.Truncate(PixelsLeft);
140 PixelsLeft -= offset;
142 CurrentPosition += Convert.ToInt32(offset);
147 BackColor = Color.Red;
149 else if (offset==1.0)
151 if (BackColor != Color.White)
153 BackColor = Color.White;
160 //BackColor = Color.Green;
163 //Only redraw if something has changed
170 void Timer_Tick(object sender, EventArgs e)
172 DateTime NewTickTime = DateTime.Now;
174 UpdateAnimation(LastTickTime, NewTickTime);
176 LastTickTime = NewTickTime;
179 private StringFormat GetStringFormatFromContentAllignment(ContentAlignment ca)
181 StringFormat format = new StringFormat(StringFormat.GenericTypographic);
184 case ContentAlignment.TopCenter:
185 format.Alignment = StringAlignment.Center;
186 format.LineAlignment = StringAlignment.Near;
188 case ContentAlignment.TopLeft:
189 format.Alignment = StringAlignment.Near;
190 format.LineAlignment = StringAlignment.Near;
192 case ContentAlignment.TopRight:
193 format.Alignment = StringAlignment.Far;
194 format.LineAlignment = StringAlignment.Near;
196 case ContentAlignment.MiddleCenter:
197 format.Alignment = StringAlignment.Center;
198 format.LineAlignment = StringAlignment.Center;
200 case ContentAlignment.MiddleLeft:
201 format.Alignment = StringAlignment.Near;
202 format.LineAlignment = StringAlignment.Center;
204 case ContentAlignment.MiddleRight:
205 format.Alignment = StringAlignment.Far;
206 format.LineAlignment = StringAlignment.Center;
208 case ContentAlignment.BottomCenter:
209 format.Alignment = StringAlignment.Center;
210 format.LineAlignment = StringAlignment.Far;
212 case ContentAlignment.BottomLeft:
213 format.Alignment = StringAlignment.Near;
214 format.LineAlignment = StringAlignment.Far;
216 case ContentAlignment.BottomRight:
217 format.Alignment = StringAlignment.Far;
218 format.LineAlignment = StringAlignment.Far;
222 format.FormatFlags |= StringFormatFlags.NoWrap;
223 format.FormatFlags |= StringFormatFlags.NoClip;
224 format.FormatFlags |= StringFormatFlags.MeasureTrailingSpaces;
225 format.Trimming = StringTrimming.None;
230 protected override void OnForeColorChanged(EventArgs e)
232 //Color has changed recreate our brush
233 iBrush = new SolidBrush(ForeColor);
235 base.OnForeColorChanged(e);
239 private void ComputeSizes()
241 //For all string measurements and drawing issues refer to the following article:
242 // http://stackoverflow.com/questions/1203087/why-is-graphics-measurestring-returning-a-higher-than-expected-number
243 //Update text size according to text and font
244 Graphics g = this.CreateGraphics();
245 g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit;
246 iStringFormat = GetStringFormatFromContentAllignment(TextAlign);
247 iTextSize = g.MeasureString(Text, iFontInUse, Int32.MaxValue, iStringFormat);
248 iSeparatorSize = g.MeasureString(Separator, iFontInUse, Int32.MaxValue, iStringFormat);
249 //Scroll width is the width of our text and our separator without taking kerning into account since
250 //both text and separator are drawn independently from each other.
251 iScrollSize.Width = iSeparatorSize.Width + iTextSize.Width;
252 iScrollSize.Height = Math.Max(iSeparatorSize.Height, iTextSize.Height); //Not relevant for now
253 //We don't want scroll width to take kerning into account so we don't use the following
254 //iScrollSize = g.MeasureString(Text + Separator, Font, Int32.MaxValue, iStringFormat);
257 private void HandleTextSizeChange()
263 if (ScaleToFit && iFontInUse.SizeInPoints > MinFontSize)
266 iFontInUse = new Font(Font.FontFamily, iFontInUse.SizeInPoints - 1, Font.Style);
267 //Recurse until we are done
268 HandleTextSizeChange();
274 //Our minimum font size still needs scrolling
275 //Reset our font then
276 iFontInUse = new Font(Font,Font.Style);
280 //Scrolling is ok or needed
281 //Always align left when scrolling
282 iStringFormat.Alignment = StringAlignment.Near;
286 //Reset our timer whenever our text changes
289 LastTickTime = DateTime.Now;
292 protected override void OnTextChanged(EventArgs e)
294 //Just clone our font
295 iFontInUse = new Font(Font, Font.Style);
297 HandleTextSizeChange();
299 base.OnTextChanged(e);
302 protected override void OnFontChanged(EventArgs e)
304 //Just clone our font
305 iFontInUse = new Font(Font,Font.Style);
307 HandleTextSizeChange();
309 base.OnFontChanged(e);
312 protected override void OnSizeChanged(EventArgs e)
314 //Just clone our font
315 iFontInUse = new Font(Font, Font.Style);
317 HandleTextSizeChange();
319 base.OnSizeChanged(e);
322 protected override void OnTextAlignChanged(EventArgs e)
324 iStringFormat = GetStringFormatFromContentAllignment(TextAlign);
327 //Always align left when scrolling to avoid bugs
328 iStringFormat.Alignment = StringAlignment.Near;
333 base.OnTextAlignChanged(e);
336 protected override void OnPaint(PaintEventArgs e)
338 //Disable anti-aliasing
339 e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit;
342 //Draw it all in a single call would take kerning into account
343 //e.Graphics.TranslateTransform(-(float)CurrentPosition, 0);
344 //e.Graphics.DrawString(Text + Separator + Text, Font, iBrush, ClientRectangle, StringFormat);
346 //Doing separate draw operation allows us not to take kerning into account between separator and string
348 e.Graphics.TranslateTransform(-(float)CurrentPosition, 0);
349 e.Graphics.DrawString(Text, iFontInUse, iBrush, ClientRectangle, iStringFormat);
351 e.Graphics.TranslateTransform(iTextSize.Width, 0);
352 e.Graphics.DrawString(Separator, iFontInUse, iBrush, ClientRectangle, iStringFormat);
354 e.Graphics.TranslateTransform(iSeparatorSize.Width, 0);
355 e.Graphics.DrawString(Text, iFontInUse, iBrush, ClientRectangle, iStringFormat);
359 e.Graphics.DrawString(Text, iFontInUse, iBrush, ClientRectangle, iStringFormat);
364 //DrawText is not working without anti-aliasing. See: stackoverflow.com/questions/8283631/graphics-drawstring-vs-textrenderer-drawtextwhich-can-deliver-better-quality
365 //TextRenderer.DrawText(e.Graphics, Text, Font, ClientRectangle, ForeColor, BackColor, iTextFormatFlags);
370 public bool NeedToScroll()
372 //if (Width < e.Graphics.MeasureString(Text, Font).Width)
373 if (Width < iTextSize.Width)
380 protected override void Dispose(bool disposing)