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