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