MarqueeLabel.cs
author sl
Wed, 09 Jul 2014 11:06:46 +0200
changeset 11 de55741d90f0
parent 8 5129c03ab7ba
child 15 452e5c0c98da
permissions -rw-r--r--
Fixing string measurement issues. Font dialog now properly initialized with
loaded font.
     1 using System;
     2 using System.Collections.Generic;
     3 using System.ComponentModel;
     4 using System.Diagnostics;
     5 using System.Linq;
     6 using System.Text;
     7 using System.Threading.Tasks;
     8 //using System.Timers;
     9 using System.Windows.Forms;
    10 using System.Drawing;
    11 
    12 namespace SharpDisplayManager
    13 {
    14     class MarqueeLabel : Label
    15     {
    16         private bool iOwnTimer;
    17         private StringFormat iStringFormat;
    18         private SolidBrush iBrush;
    19         private SizeF iTextSize;
    20         private SizeF iSeparatorSize;
    21 
    22         [Category("Appearance")]
    23         [Description("Separator in our scrolling loop.")]
    24         [DefaultValue(" | ")]
    25         [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
    26         [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
    27         public string Separator { get; set; }
    28 
    29         [Category("Behavior")]
    30         [Description("How fast is our text scrolling, in pixels per second.")]
    31         [DefaultValue(32)]
    32         [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
    33         [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
    34         public int PixelsPerSecond { get; set; }
    35 
    36         [Category("Behavior")]
    37         [Description("Use an internal or an external timer.")]
    38         [DefaultValue(true)]
    39         [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
    40         [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
    41         public bool OwnTimer
    42         {
    43             get
    44             {
    45                 return iOwnTimer;
    46             }
    47             set
    48             {
    49                 iOwnTimer = value;
    50 
    51                 if (iOwnTimer)
    52                 {
    53                     Timer = new Timer();
    54                     Timer.Interval = 10;
    55                     Timer.Tick += new EventHandler(Timer_Tick);
    56                     Timer.Start();
    57                 }
    58                 else
    59                 {
    60                     if (Timer != null)
    61                         Timer.Dispose();
    62                     Timer = null;
    63                 }
    64 
    65             }
    66         }
    67 
    68         private int CurrentPosition { get; set; }
    69         private Timer Timer { get; set; }
    70         private DateTime LastTickTime { get; set; }
    71         private double PixelsLeft { get; set; }
    72         //DateTime a = new DateTime(2010, 05, 12, 13, 15, 00);
    73         //DateTime b = new DateTime(2010, 05, 12, 13, 45, 00);
    74         //Console.WriteLine(b.Subtract(a).TotalMinutes);
    75 
    76         public MarqueeLabel()
    77         {
    78             UseCompatibleTextRendering = true;
    79             //PixelsPerSecond = 32;
    80             LastTickTime = DateTime.Now;
    81             PixelsLeft = 0;
    82             iBrush = new SolidBrush(ForeColor);
    83         }
    84 
    85         public void UpdateAnimation(DateTime aLastTickTime, DateTime aNewTickTime)
    86         {
    87             if (!NeedToScroll())
    88             {
    89                 CurrentPosition = 0;
    90                 return;
    91             }
    92 
    93             while (CurrentPosition > (iTextSize.Width + iSeparatorSize.Width))
    94             {
    95                 CurrentPosition -= ((int)(iTextSize.Width + iSeparatorSize.Width));
    96             }
    97 
    98             PixelsLeft += aNewTickTime.Subtract(aLastTickTime).TotalSeconds * PixelsPerSecond;
    99 
   100             //Keep track of our pixels left over
   101             //PixelsLeft = offset - Math.Truncate(offset);
   102             double offset = Math.Truncate(PixelsLeft);
   103             PixelsLeft -= offset;
   104 
   105             CurrentPosition += Convert.ToInt32(offset);
   106 
   107             /*
   108             if (offset > 1.0)
   109             {
   110                 BackColor = Color.Red;
   111             }
   112             else if (offset==1.0)
   113             {
   114                 if (BackColor != Color.White)
   115                 {
   116                     BackColor = Color.White;
   117                 }
   118 
   119             }
   120             else
   121             {
   122                 //Too slow
   123                 //BackColor = Color.Green;
   124             }*/
   125 
   126             //Only redraw if something has changed
   127             if (offset != 0)
   128             {
   129                 Invalidate();
   130             }
   131         }
   132 
   133         void Timer_Tick(object sender, EventArgs e)
   134         {
   135             DateTime NewTickTime = DateTime.Now;
   136             //
   137             UpdateAnimation(LastTickTime, NewTickTime);
   138             //
   139             LastTickTime = NewTickTime;
   140         }
   141 
   142         private StringFormat GetStringFormatFromContentAllignment(ContentAlignment ca)
   143         {
   144             StringFormat format = new StringFormat();
   145             format = StringFormat.GenericTypographic;
   146             switch (ca)
   147             {
   148                 case ContentAlignment.TopCenter:
   149                     format.Alignment = StringAlignment.Near;
   150                     format.LineAlignment = StringAlignment.Center;
   151                     break;
   152                 case ContentAlignment.TopLeft:
   153                     format.Alignment = StringAlignment.Near;
   154                     format.LineAlignment = StringAlignment.Near;
   155                     break;
   156                 case ContentAlignment.TopRight:
   157                     format.Alignment = StringAlignment.Near;
   158                     format.LineAlignment = StringAlignment.Far;
   159                     break;
   160                 case ContentAlignment.MiddleCenter:
   161                     format.Alignment = StringAlignment.Center;
   162                     format.LineAlignment = StringAlignment.Center;
   163                     break;
   164                 case ContentAlignment.MiddleLeft:
   165                     format.Alignment = StringAlignment.Center;
   166                     format.LineAlignment = StringAlignment.Near;
   167                     break;
   168                 case ContentAlignment.MiddleRight:
   169                     format.Alignment = StringAlignment.Center;
   170                     format.LineAlignment = StringAlignment.Far;
   171                     break;
   172                 case ContentAlignment.BottomCenter:
   173                     format.Alignment = StringAlignment.Far;
   174                     format.LineAlignment = StringAlignment.Center;
   175                     break;
   176                 case ContentAlignment.BottomLeft:
   177                     format.Alignment = StringAlignment.Far;
   178                     format.LineAlignment = StringAlignment.Near;
   179                     break;
   180                 case ContentAlignment.BottomRight:
   181                     format.Alignment = StringAlignment.Far;
   182                     format.LineAlignment = StringAlignment.Far;
   183                     break;
   184             }
   185 
   186             format.FormatFlags |= StringFormatFlags.NoWrap;
   187             format.FormatFlags |= StringFormatFlags.NoClip;
   188             format.FormatFlags |= StringFormatFlags.MeasureTrailingSpaces;
   189             format.Trimming = StringTrimming.None;
   190 
   191             return format;
   192         }
   193 
   194         protected override void OnForeColorChanged(EventArgs e)
   195         {
   196             //Color has changed recreate our brush
   197             iBrush = new SolidBrush(ForeColor);
   198 
   199             base.OnForeColorChanged(e);
   200         }
   201 
   202 
   203         private void HandleTextSizeChange()
   204         {
   205             //For all string measurements and drawing issues refer to the following article:
   206             // http://stackoverflow.com/questions/1203087/why-is-graphics-measurestring-returning-a-higher-than-expected-number
   207             //Update text size according to text and font
   208             Graphics g = this.CreateGraphics();
   209             g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit;
   210             iStringFormat = GetStringFormatFromContentAllignment(TextAlign);
   211             iTextSize = g.MeasureString(Text, Font, Int32.MaxValue, iStringFormat);
   212             iSeparatorSize = g.MeasureString(Separator, Font, Int32.MaxValue, iStringFormat);
   213 
   214             if (NeedToScroll())
   215             {
   216                 //Always align left when scrolling
   217                 iStringFormat.Alignment = StringAlignment.Near;
   218             }
   219         }
   220 
   221         protected override void OnTextChanged(EventArgs e)
   222         {
   223             HandleTextSizeChange();
   224 
   225             base.OnTextChanged(e);
   226         }
   227 
   228         protected override void OnFontChanged(EventArgs e)
   229         {
   230             HandleTextSizeChange();
   231 
   232             base.OnFontChanged(e);
   233         }
   234 
   235         protected override void OnTextAlignChanged(EventArgs e)
   236         {
   237             iStringFormat = GetStringFormatFromContentAllignment(TextAlign);
   238 
   239             base.OnTextAlignChanged(e);
   240 
   241         }
   242 
   243         protected override void OnPaint(PaintEventArgs e)
   244         {
   245             //Disable anti-aliasing
   246             e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit;
   247             if (NeedToScroll())
   248             {
   249                 //Draw the first one
   250                 e.Graphics.TranslateTransform(-(float)CurrentPosition, 0);
   251                 e.Graphics.DrawString(Text, Font, iBrush, ClientRectangle, iStringFormat);
   252                 //Draw separator
   253                 e.Graphics.TranslateTransform(iTextSize.Width, 0);
   254                 e.Graphics.DrawString(Separator, Font, iBrush, ClientRectangle, iStringFormat);
   255                 //Draw the last one
   256                 e.Graphics.TranslateTransform(iSeparatorSize.Width, 0);
   257                 e.Graphics.DrawString(Text, Font, iBrush, ClientRectangle, iStringFormat);
   258             }
   259             else
   260             {
   261                 e.Graphics.DrawString(Text, Font, iBrush, ClientRectangle, iStringFormat);
   262             }
   263 
   264 
   265 
   266             //DrawText is not working without anti-aliasing. See: stackoverflow.com/questions/8283631/graphics-drawstring-vs-textrenderer-drawtextwhich-can-deliver-better-quality
   267             //TextRenderer.DrawText(e.Graphics, Text, Font, ClientRectangle, ForeColor, BackColor, iTextFormatFlags);
   268 
   269             //base.OnPaint(e);
   270         }
   271 
   272         public bool NeedToScroll()
   273         {
   274             //if (Width < e.Graphics.MeasureString(Text, Font).Width)
   275             if (Width < iTextSize.Width)
   276             {
   277                 return true;
   278             }
   279             return false;
   280         }
   281 
   282         protected override void Dispose(bool disposing)
   283         {
   284             if (disposing)
   285             {
   286                 if (Timer != null)
   287                     Timer.Dispose();
   288             }
   289             Timer = null;
   290         }
   291     }
   292 }