Server/MarqueeLabel.cs
author StephaneLenclud
Mon, 18 Aug 2014 23:43:44 +0200
changeset 35 f3893924a6eb
parent 34 59bfa4ebcbbb
child 41 1864e4fd1728
permissions -rw-r--r--
Tried using Control instead of Label as base class for our Marquee.
Sticking to Label for now.
     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     public 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         private SizeF iScrollSize;
    23         //private ContentAlignment iRequestedContentAlignment;
    24 
    25         [Category("Appearance")]
    26         [Description("Separator in our scrolling loop.")]
    27         [DefaultValue(" | ")]
    28         [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
    29         [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
    30         public string Separator { get; set; }
    31 
    32         [Category("Behavior")]
    33         [Description("How fast is our text scrolling, in pixels per second.")]
    34         [DefaultValue(32)]
    35         [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
    36         [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
    37         public int PixelsPerSecond { get; set; }
    38 
    39         [Category("Behavior")]
    40         [Description("Use an internal or an external timer.")]
    41         [DefaultValue(true)]
    42         [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
    43         [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
    44         public bool OwnTimer
    45         {
    46             get
    47             {
    48                 return iOwnTimer;
    49             }
    50             set
    51             {
    52                 iOwnTimer = value;
    53 
    54                 if (iOwnTimer)
    55                 {
    56                     Timer = new Timer();
    57                     Timer.Interval = 10;
    58                     Timer.Tick += new EventHandler(Timer_Tick);
    59                     Timer.Start();
    60                 }
    61                 else
    62                 {
    63                     if (Timer != null)
    64                         Timer.Dispose();
    65                     Timer = null;
    66                 }
    67 
    68             }
    69         }
    70 
    71         private int CurrentPosition { get; set; }
    72         private Timer Timer { get; set; }
    73         private DateTime LastTickTime { get; set; }
    74         private double PixelsLeft { get; set; }
    75         //DateTime a = new DateTime(2010, 05, 12, 13, 15, 00);
    76         //DateTime b = new DateTime(2010, 05, 12, 13, 45, 00);
    77         //Console.WriteLine(b.Subtract(a).TotalMinutes);
    78 
    79         public MarqueeLabel()
    80         {
    81             UseCompatibleTextRendering = true;
    82             //PixelsPerSecond = 32;
    83             LastTickTime = DateTime.Now;
    84             PixelsLeft = 0;
    85             CurrentPosition = 0;
    86             iBrush = new SolidBrush(ForeColor);
    87             //iRequestedContentAlignment = TextAlign;
    88 
    89             //Following is needed if we ever switch from Label to Control base class. 
    90             //Without it you get some pretty nasty flicker
    91             //SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
    92             //SetStyle(ControlStyles.UserPaint, true);
    93             //SetStyle(ControlStyles.AllPaintingInWmPaint, true); 
    94             //SetStyle(ControlStyles.DoubleBuffer, true);
    95         }
    96 
    97         public void UpdateAnimation(DateTime aLastTickTime, DateTime aNewTickTime)
    98         {
    99             if (!NeedToScroll())
   100             {
   101                 CurrentPosition = 0;
   102                 return;
   103             }
   104 
   105             /*
   106             while (CurrentPosition > (iTextSize.Width + iSeparatorSize.Width))
   107             {
   108                 CurrentPosition -= ((int)(iTextSize.Width + iSeparatorSize.Width));
   109             }
   110              */
   111 
   112             while (CurrentPosition > iScrollSize.Width)
   113             {
   114                 CurrentPosition -= (int)iScrollSize.Width;
   115             }
   116 
   117 
   118             PixelsLeft += aNewTickTime.Subtract(aLastTickTime).TotalSeconds * PixelsPerSecond;
   119 
   120             //Keep track of our pixels left over
   121             //PixelsLeft = offset - Math.Truncate(offset);
   122             double offset = Math.Truncate(PixelsLeft);
   123             PixelsLeft -= offset;
   124 
   125             CurrentPosition += Convert.ToInt32(offset);
   126 
   127             /*
   128             if (offset > 1.0)
   129             {
   130                 BackColor = Color.Red;
   131             }
   132             else if (offset==1.0)
   133             {
   134                 if (BackColor != Color.White)
   135                 {
   136                     BackColor = Color.White;
   137                 }
   138 
   139             }
   140             else
   141             {
   142                 //Too slow
   143                 //BackColor = Color.Green;
   144             }*/
   145 
   146             //Only redraw if something has changed
   147             if (offset != 0)
   148             {
   149                 Invalidate();
   150             }
   151         }
   152 
   153         void Timer_Tick(object sender, EventArgs e)
   154         {
   155             DateTime NewTickTime = DateTime.Now;
   156             //
   157             UpdateAnimation(LastTickTime, NewTickTime);
   158             //
   159             LastTickTime = NewTickTime;
   160         }
   161 
   162         private StringFormat GetStringFormatFromContentAllignment(ContentAlignment ca)
   163         {
   164             StringFormat format = new StringFormat();
   165             format = StringFormat.GenericTypographic;
   166             switch (ca)
   167             {
   168                 case ContentAlignment.TopCenter:
   169                     format.Alignment = StringAlignment.Center;
   170                     format.LineAlignment = StringAlignment.Near;
   171                     break;
   172                 case ContentAlignment.TopLeft:
   173                     format.Alignment = StringAlignment.Near;
   174                     format.LineAlignment = StringAlignment.Near;
   175                     break;
   176                 case ContentAlignment.TopRight:
   177                     format.Alignment = StringAlignment.Far;
   178                     format.LineAlignment = StringAlignment.Near;
   179                     break;
   180                 case ContentAlignment.MiddleCenter:
   181                     format.Alignment = StringAlignment.Center;
   182                     format.LineAlignment = StringAlignment.Center;
   183                     break;
   184                 case ContentAlignment.MiddleLeft:
   185                     format.Alignment = StringAlignment.Near;
   186                     format.LineAlignment = StringAlignment.Center;
   187                     break;
   188                 case ContentAlignment.MiddleRight:
   189                     format.Alignment = StringAlignment.Far;
   190                     format.LineAlignment = StringAlignment.Center;
   191                     break;
   192                 case ContentAlignment.BottomCenter:
   193                     format.Alignment = StringAlignment.Center;
   194                     format.LineAlignment = StringAlignment.Far;
   195                     break;
   196                 case ContentAlignment.BottomLeft:
   197                     format.Alignment = StringAlignment.Near;
   198                     format.LineAlignment = StringAlignment.Far;
   199                     break;
   200                 case ContentAlignment.BottomRight:
   201                     format.Alignment = StringAlignment.Far;
   202                     format.LineAlignment = StringAlignment.Far;
   203                     break;
   204             }
   205 
   206             format.FormatFlags |= StringFormatFlags.NoWrap;
   207             format.FormatFlags |= StringFormatFlags.NoClip;
   208             format.FormatFlags |= StringFormatFlags.MeasureTrailingSpaces;
   209             format.Trimming = StringTrimming.None;
   210 
   211             return format;
   212         }
   213 
   214         protected override void OnForeColorChanged(EventArgs e)
   215         {
   216             //Color has changed recreate our brush
   217             iBrush = new SolidBrush(ForeColor);
   218 
   219             base.OnForeColorChanged(e);
   220         }
   221 
   222 
   223         private void HandleTextSizeChange()
   224         {
   225             //Reset our timer whenever our text changes
   226             CurrentPosition = 0;
   227             LastTickTime = DateTime.Now;
   228             PixelsLeft = 0;
   229             //Reset text align
   230             //TextAlign = iRequestedContentAlignment;
   231 
   232             //For all string measurements and drawing issues refer to the following article:
   233             // http://stackoverflow.com/questions/1203087/why-is-graphics-measurestring-returning-a-higher-than-expected-number
   234             //Update text size according to text and font
   235             Graphics g = this.CreateGraphics();
   236             g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit;
   237             iStringFormat = GetStringFormatFromContentAllignment(TextAlign);
   238             iTextSize = g.MeasureString(Text, Font, Int32.MaxValue, iStringFormat);
   239             iSeparatorSize = g.MeasureString(Separator, Font, Int32.MaxValue, iStringFormat);
   240             //Scroll width is the width of our text and our separator without taking kerning into account since
   241             //both text and separator are drawn independently from each other.
   242             iScrollSize.Width = iSeparatorSize.Width + iTextSize.Width;
   243             iScrollSize.Height = Math.Max(iSeparatorSize.Height, iTextSize.Height); //Not relevant for now
   244             //We don't want scroll with to take kerning into account so we don't use the following
   245             //iScrollSize = g.MeasureString(Text + Separator, Font, Int32.MaxValue, iStringFormat);
   246 
   247             if (NeedToScroll())
   248             {
   249                 //Always align left when scrolling
   250                 //Somehow draw string still takes into our control alignment so we need to set it too
   251                 //ContentAlignment original = TextAlign;
   252                 TextAlign = ContentAlignment.MiddleLeft;
   253                 //Make sure our original text alignment remain the same even though we override it when scrolling
   254                 //iRequestedContentAlignment = original; 
   255                 //iStringFormat will get updated in OnTextAlignChanged
   256                 //iStringFormat.Alignment = StringAlignment.Near;
   257             }
   258         }
   259 
   260         protected override void OnTextChanged(EventArgs e)
   261         {
   262             HandleTextSizeChange();
   263 
   264             base.OnTextChanged(e);
   265         }
   266 
   267         protected override void OnFontChanged(EventArgs e)
   268         {
   269             HandleTextSizeChange();
   270 
   271             base.OnFontChanged(e);
   272         }
   273 
   274         protected override void OnTextAlignChanged(EventArgs e)
   275         {
   276             iStringFormat = GetStringFormatFromContentAllignment(TextAlign);
   277             //iRequestedContentAlignment = TextAlign;
   278             base.OnTextAlignChanged(e);
   279 
   280         }
   281 
   282         protected override void OnPaint(PaintEventArgs e)
   283         {
   284             //Disable anti-aliasing
   285             e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit;
   286             if (NeedToScroll())
   287             {
   288                 //Draw it all in a single call would take kerning into account
   289                 //e.Graphics.TranslateTransform(-(float)CurrentPosition, 0);
   290                 //e.Graphics.DrawString(Text + Separator + Text, Font, iBrush, ClientRectangle, iStringFormat);
   291 
   292                 //Doing separate draw operation allows us not to take kerning into account between separator and string
   293                 //Draw the first one
   294                 e.Graphics.TranslateTransform(-(float)CurrentPosition, 0);
   295                 e.Graphics.DrawString(Text, Font, iBrush, ClientRectangle, iStringFormat);
   296                 //Draw separator
   297                 e.Graphics.TranslateTransform(iTextSize.Width, 0);
   298                 e.Graphics.DrawString(Separator, Font, iBrush, ClientRectangle, iStringFormat);
   299                 //Draw the last one
   300                 e.Graphics.TranslateTransform(iSeparatorSize.Width, 0);
   301                 e.Graphics.DrawString(Text, Font, iBrush, ClientRectangle, iStringFormat);
   302             }
   303             else
   304             {
   305                 e.Graphics.DrawString(Text, Font, iBrush, ClientRectangle, iStringFormat);
   306             }
   307 
   308 
   309 
   310             //DrawText is not working without anti-aliasing. See: stackoverflow.com/questions/8283631/graphics-drawstring-vs-textrenderer-drawtextwhich-can-deliver-better-quality
   311             //TextRenderer.DrawText(e.Graphics, Text, Font, ClientRectangle, ForeColor, BackColor, iTextFormatFlags);
   312 
   313             //base.OnPaint(e);
   314         }
   315 
   316         public bool NeedToScroll()
   317         {
   318             //if (Width < e.Graphics.MeasureString(Text, Font).Width)
   319             if (Width < iTextSize.Width)
   320             {
   321                 return true;
   322             }
   323             return false;
   324         }
   325 
   326         protected override void Dispose(bool disposing)
   327         {
   328             if (disposing)
   329             {
   330                 if (Timer != null)
   331                     Timer.Dispose();
   332             }
   333             Timer = null;
   334         }
   335     }
   336 }