Server/MarqueeLabel.cs
author sl
Sun, 17 Aug 2014 22:30:30 +0200
changeset 34 59bfa4ebcbbb
parent 33 1363bda20171
child 35 f3893924a6eb
permissions -rw-r--r--
Adding text field support to our tree view.
Trying to fix our messy text alignment issue causing loop problems.
     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 
    90         public void UpdateAnimation(DateTime aLastTickTime, DateTime aNewTickTime)
    91         {
    92             if (!NeedToScroll())
    93             {
    94                 CurrentPosition = 0;
    95                 return;
    96             }
    97 
    98             /*
    99             while (CurrentPosition > (iTextSize.Width + iSeparatorSize.Width))
   100             {
   101                 CurrentPosition -= ((int)(iTextSize.Width + iSeparatorSize.Width));
   102             }
   103              */
   104 
   105             while (CurrentPosition > iScrollSize.Width)
   106             {
   107                 CurrentPosition -= (int)iScrollSize.Width;
   108             }
   109 
   110 
   111             PixelsLeft += aNewTickTime.Subtract(aLastTickTime).TotalSeconds * PixelsPerSecond;
   112 
   113             //Keep track of our pixels left over
   114             //PixelsLeft = offset - Math.Truncate(offset);
   115             double offset = Math.Truncate(PixelsLeft);
   116             PixelsLeft -= offset;
   117 
   118             CurrentPosition += Convert.ToInt32(offset);
   119 
   120             /*
   121             if (offset > 1.0)
   122             {
   123                 BackColor = Color.Red;
   124             }
   125             else if (offset==1.0)
   126             {
   127                 if (BackColor != Color.White)
   128                 {
   129                     BackColor = Color.White;
   130                 }
   131 
   132             }
   133             else
   134             {
   135                 //Too slow
   136                 //BackColor = Color.Green;
   137             }*/
   138 
   139             //Only redraw if something has changed
   140             if (offset != 0)
   141             {
   142                 Invalidate();
   143             }
   144         }
   145 
   146         void Timer_Tick(object sender, EventArgs e)
   147         {
   148             DateTime NewTickTime = DateTime.Now;
   149             //
   150             UpdateAnimation(LastTickTime, NewTickTime);
   151             //
   152             LastTickTime = NewTickTime;
   153         }
   154 
   155         private StringFormat GetStringFormatFromContentAllignment(ContentAlignment ca)
   156         {
   157             StringFormat format = new StringFormat();
   158             format = StringFormat.GenericTypographic;
   159             switch (ca)
   160             {
   161                 case ContentAlignment.TopCenter:
   162                     format.Alignment = StringAlignment.Center;
   163                     format.LineAlignment = StringAlignment.Near;
   164                     break;
   165                 case ContentAlignment.TopLeft:
   166                     format.Alignment = StringAlignment.Near;
   167                     format.LineAlignment = StringAlignment.Near;
   168                     break;
   169                 case ContentAlignment.TopRight:
   170                     format.Alignment = StringAlignment.Far;
   171                     format.LineAlignment = StringAlignment.Near;
   172                     break;
   173                 case ContentAlignment.MiddleCenter:
   174                     format.Alignment = StringAlignment.Center;
   175                     format.LineAlignment = StringAlignment.Center;
   176                     break;
   177                 case ContentAlignment.MiddleLeft:
   178                     format.Alignment = StringAlignment.Near;
   179                     format.LineAlignment = StringAlignment.Center;
   180                     break;
   181                 case ContentAlignment.MiddleRight:
   182                     format.Alignment = StringAlignment.Far;
   183                     format.LineAlignment = StringAlignment.Center;
   184                     break;
   185                 case ContentAlignment.BottomCenter:
   186                     format.Alignment = StringAlignment.Center;
   187                     format.LineAlignment = StringAlignment.Far;
   188                     break;
   189                 case ContentAlignment.BottomLeft:
   190                     format.Alignment = StringAlignment.Near;
   191                     format.LineAlignment = StringAlignment.Far;
   192                     break;
   193                 case ContentAlignment.BottomRight:
   194                     format.Alignment = StringAlignment.Far;
   195                     format.LineAlignment = StringAlignment.Far;
   196                     break;
   197             }
   198 
   199             format.FormatFlags |= StringFormatFlags.NoWrap;
   200             format.FormatFlags |= StringFormatFlags.NoClip;
   201             format.FormatFlags |= StringFormatFlags.MeasureTrailingSpaces;
   202             format.Trimming = StringTrimming.None;
   203 
   204             return format;
   205         }
   206 
   207         protected override void OnForeColorChanged(EventArgs e)
   208         {
   209             //Color has changed recreate our brush
   210             iBrush = new SolidBrush(ForeColor);
   211 
   212             base.OnForeColorChanged(e);
   213         }
   214 
   215 
   216         private void HandleTextSizeChange()
   217         {
   218             //Reset our timer whenever our text changes
   219             CurrentPosition = 0;
   220             LastTickTime = DateTime.Now;
   221             PixelsLeft = 0;
   222             //Reset text align
   223             //TextAlign = iRequestedContentAlignment;
   224 
   225             //For all string measurements and drawing issues refer to the following article:
   226             // http://stackoverflow.com/questions/1203087/why-is-graphics-measurestring-returning-a-higher-than-expected-number
   227             //Update text size according to text and font
   228             Graphics g = this.CreateGraphics();
   229             g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit;
   230             iStringFormat = GetStringFormatFromContentAllignment(TextAlign);
   231             iTextSize = g.MeasureString(Text, Font, Int32.MaxValue, iStringFormat);
   232             iSeparatorSize = g.MeasureString(Separator, Font, Int32.MaxValue, iStringFormat);
   233             //Scroll width is the width of our text and our separator without taking kerning into account since
   234             //both text and separator are drawn independently from each other.
   235             iScrollSize.Width = iSeparatorSize.Width + iTextSize.Width;
   236             iScrollSize.Height = Math.Max(iSeparatorSize.Height, iTextSize.Height); //Not relevant for now
   237             //We don't want scroll with to take kerning into account so we don't use the following
   238             //iScrollSize = g.MeasureString(Text + Separator, Font, Int32.MaxValue, iStringFormat);
   239 
   240             if (NeedToScroll())
   241             {
   242                 //Always align left when scrolling
   243                 //Somehow draw string still takes into our control alignment so we need to set it too
   244                 //ContentAlignment original = TextAlign;
   245                 TextAlign = ContentAlignment.MiddleLeft;
   246                 //Make sure our original text alignment remain the same even though we override it when scrolling
   247                 //iRequestedContentAlignment = original; 
   248                 //iStringFormat will get updated in OnTextAlignChanged
   249                 //iStringFormat.Alignment = StringAlignment.Near;
   250             }
   251         }
   252 
   253         protected override void OnTextChanged(EventArgs e)
   254         {
   255             HandleTextSizeChange();
   256 
   257             base.OnTextChanged(e);
   258         }
   259 
   260         protected override void OnFontChanged(EventArgs e)
   261         {
   262             HandleTextSizeChange();
   263 
   264             base.OnFontChanged(e);
   265         }
   266 
   267         protected override void OnTextAlignChanged(EventArgs e)
   268         {
   269             iStringFormat = GetStringFormatFromContentAllignment(TextAlign);
   270             //iRequestedContentAlignment = TextAlign;
   271             base.OnTextAlignChanged(e);
   272 
   273         }
   274 
   275         protected override void OnPaint(PaintEventArgs e)
   276         {
   277             //Disable anti-aliasing
   278             e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit;
   279             if (NeedToScroll())
   280             {
   281                 //Draw it all in a single call would take kerning into account
   282                 //e.Graphics.TranslateTransform(-(float)CurrentPosition, 0);
   283                 //e.Graphics.DrawString(Text + Separator + Text, Font, iBrush, ClientRectangle, iStringFormat);
   284 
   285                 //Doing separate draw operation allows us not to take kerning into account between separator and string
   286                 //Draw the first one
   287                 e.Graphics.TranslateTransform(-(float)CurrentPosition, 0);
   288                 e.Graphics.DrawString(Text, Font, iBrush, ClientRectangle, iStringFormat);
   289                 //Draw separator
   290                 e.Graphics.TranslateTransform(iTextSize.Width, 0);
   291                 e.Graphics.DrawString(Separator, Font, iBrush, ClientRectangle, iStringFormat);
   292                 //Draw the last one
   293                 e.Graphics.TranslateTransform(iSeparatorSize.Width, 0);
   294                 e.Graphics.DrawString(Text, Font, iBrush, ClientRectangle, iStringFormat);
   295             }
   296             else
   297             {
   298                 e.Graphics.DrawString(Text, Font, iBrush, ClientRectangle, iStringFormat);
   299             }
   300 
   301 
   302 
   303             //DrawText is not working without anti-aliasing. See: stackoverflow.com/questions/8283631/graphics-drawstring-vs-textrenderer-drawtextwhich-can-deliver-better-quality
   304             //TextRenderer.DrawText(e.Graphics, Text, Font, ClientRectangle, ForeColor, BackColor, iTextFormatFlags);
   305 
   306             //base.OnPaint(e);
   307         }
   308 
   309         public bool NeedToScroll()
   310         {
   311             //if (Width < e.Graphics.MeasureString(Text, Font).Width)
   312             if (Width < iTextSize.Width)
   313             {
   314                 return true;
   315             }
   316             return false;
   317         }
   318 
   319         protected override void Dispose(bool disposing)
   320         {
   321             if (disposing)
   322             {
   323                 if (Timer != null)
   324                     Timer.Dispose();
   325             }
   326             Timer = null;
   327         }
   328     }
   329 }