Support row and column span.
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;
24 [Category("Appearance")]
25 [Description("Separator in our scrolling loop.")]
27 [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
28 [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
29 public string Separator { get; set; }
31 [Category("Behavior")]
32 [Description("How fast is our text scrolling, in pixels per second.")]
34 [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
35 [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
36 public int PixelsPerSecond { get; set; }
38 [Category("Behavior")]
39 [Description("Use an internal or an external timer.")]
41 [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
42 [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
57 Timer.Tick += new EventHandler(Timer_Tick);
70 private int CurrentPosition { get; set; }
71 private Timer Timer { get; set; }
72 private DateTime LastTickTime { get; set; }
73 private double PixelsLeft { get; set; }
74 //DateTime a = new DateTime(2010, 05, 12, 13, 15, 00);
75 //DateTime b = new DateTime(2010, 05, 12, 13, 45, 00);
76 //Console.WriteLine(b.Subtract(a).TotalMinutes);
80 UseCompatibleTextRendering = true;
81 //PixelsPerSecond = 32;
82 LastTickTime = DateTime.Now;
85 iBrush = new SolidBrush(ForeColor);
87 //Following is needed if we ever switch from Label to Control base class.
88 //Without it you get some pretty nasty flicker
89 //SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
90 //SetStyle(ControlStyles.UserPaint, true);
91 //SetStyle(ControlStyles.AllPaintingInWmPaint, true);
92 //SetStyle(ControlStyles.DoubleBuffer, true);
95 public void UpdateAnimation(DateTime aLastTickTime, DateTime aNewTickTime)
104 while (CurrentPosition > (iTextSize.Width + iSeparatorSize.Width))
106 CurrentPosition -= ((int)(iTextSize.Width + iSeparatorSize.Width));
110 while (CurrentPosition > iScrollSize.Width)
112 CurrentPosition -= (int)iScrollSize.Width;
116 PixelsLeft += aNewTickTime.Subtract(aLastTickTime).TotalSeconds * PixelsPerSecond;
118 //Keep track of our pixels left over
119 //PixelsLeft = offset - Math.Truncate(offset);
120 double offset = Math.Truncate(PixelsLeft);
121 PixelsLeft -= offset;
123 CurrentPosition += Convert.ToInt32(offset);
128 BackColor = Color.Red;
130 else if (offset==1.0)
132 if (BackColor != Color.White)
134 BackColor = Color.White;
141 //BackColor = Color.Green;
144 //Only redraw if something has changed
151 void Timer_Tick(object sender, EventArgs e)
153 DateTime NewTickTime = DateTime.Now;
155 UpdateAnimation(LastTickTime, NewTickTime);
157 LastTickTime = NewTickTime;
160 private StringFormat GetStringFormatFromContentAllignment(ContentAlignment ca)
162 StringFormat format = new StringFormat(StringFormat.GenericTypographic);
165 case ContentAlignment.TopCenter:
166 format.Alignment = StringAlignment.Center;
167 format.LineAlignment = StringAlignment.Near;
169 case ContentAlignment.TopLeft:
170 format.Alignment = StringAlignment.Near;
171 format.LineAlignment = StringAlignment.Near;
173 case ContentAlignment.TopRight:
174 format.Alignment = StringAlignment.Far;
175 format.LineAlignment = StringAlignment.Near;
177 case ContentAlignment.MiddleCenter:
178 format.Alignment = StringAlignment.Center;
179 format.LineAlignment = StringAlignment.Center;
181 case ContentAlignment.MiddleLeft:
182 format.Alignment = StringAlignment.Near;
183 format.LineAlignment = StringAlignment.Center;
185 case ContentAlignment.MiddleRight:
186 format.Alignment = StringAlignment.Far;
187 format.LineAlignment = StringAlignment.Center;
189 case ContentAlignment.BottomCenter:
190 format.Alignment = StringAlignment.Center;
191 format.LineAlignment = StringAlignment.Far;
193 case ContentAlignment.BottomLeft:
194 format.Alignment = StringAlignment.Near;
195 format.LineAlignment = StringAlignment.Far;
197 case ContentAlignment.BottomRight:
198 format.Alignment = StringAlignment.Far;
199 format.LineAlignment = StringAlignment.Far;
203 format.FormatFlags |= StringFormatFlags.NoWrap;
204 format.FormatFlags |= StringFormatFlags.NoClip;
205 format.FormatFlags |= StringFormatFlags.MeasureTrailingSpaces;
206 format.Trimming = StringTrimming.None;
211 protected override void OnForeColorChanged(EventArgs e)
213 //Color has changed recreate our brush
214 iBrush = new SolidBrush(ForeColor);
216 base.OnForeColorChanged(e);
220 private void HandleTextSizeChange()
222 //Reset our timer whenever our text changes
224 LastTickTime = DateTime.Now;
227 //For all string measurements and drawing issues refer to the following article:
228 // http://stackoverflow.com/questions/1203087/why-is-graphics-measurestring-returning-a-higher-than-expected-number
229 //Update text size according to text and font
230 Graphics g = this.CreateGraphics();
231 g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit;
232 iStringFormat = GetStringFormatFromContentAllignment(TextAlign);
233 iTextSize = g.MeasureString(Text, Font, Int32.MaxValue, iStringFormat);
234 iSeparatorSize = g.MeasureString(Separator, Font, Int32.MaxValue, iStringFormat);
235 //Scroll width is the width of our text and our separator without taking kerning into account since
236 //both text and separator are drawn independently from each other.
237 iScrollSize.Width = iSeparatorSize.Width + iTextSize.Width;
238 iScrollSize.Height = Math.Max(iSeparatorSize.Height, iTextSize.Height); //Not relevant for now
239 //We don't want scroll with to take kerning into account so we don't use the following
240 //iScrollSize = g.MeasureString(Text + Separator, Font, Int32.MaxValue, iStringFormat);
244 //Always align left when scrolling
245 iStringFormat.Alignment = StringAlignment.Near;
250 protected override void OnTextChanged(EventArgs e)
252 HandleTextSizeChange();
254 base.OnTextChanged(e);
257 protected override void OnFontChanged(EventArgs e)
259 HandleTextSizeChange();
261 base.OnFontChanged(e);
264 protected override void OnTextAlignChanged(EventArgs e)
266 iStringFormat = GetStringFormatFromContentAllignment(TextAlign);
269 //Always align left when scrolling to avoid bugs
270 iStringFormat.Alignment = StringAlignment.Near;
275 base.OnTextAlignChanged(e);
278 protected override void OnPaint(PaintEventArgs e)
280 //Disable anti-aliasing
281 e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit;
284 //Draw it all in a single call would take kerning into account
285 //e.Graphics.TranslateTransform(-(float)CurrentPosition, 0);
286 //e.Graphics.DrawString(Text + Separator + Text, Font, iBrush, ClientRectangle, StringFormat);
288 //Doing separate draw operation allows us not to take kerning into account between separator and string
290 e.Graphics.TranslateTransform(-(float)CurrentPosition, 0);
291 e.Graphics.DrawString(Text, Font, iBrush, ClientRectangle, iStringFormat);
293 e.Graphics.TranslateTransform(iTextSize.Width, 0);
294 e.Graphics.DrawString(Separator, Font, iBrush, ClientRectangle, iStringFormat);
296 e.Graphics.TranslateTransform(iSeparatorSize.Width, 0);
297 e.Graphics.DrawString(Text, Font, iBrush, ClientRectangle, iStringFormat);
301 e.Graphics.DrawString(Text, Font, iBrush, ClientRectangle, iStringFormat);
306 //DrawText is not working without anti-aliasing. See: stackoverflow.com/questions/8283631/graphics-drawstring-vs-textrenderer-drawtextwhich-can-deliver-better-quality
307 //TextRenderer.DrawText(e.Graphics, Text, Font, ClientRectangle, ForeColor, BackColor, iTextFormatFlags);
312 public bool NeedToScroll()
314 //if (Width < e.Graphics.MeasureString(Text, Font).Width)
315 if (Width < iTextSize.Width)
322 protected override void Dispose(bool disposing)