简体   繁体   中英

Implementing news ticker in Compact Framework - Unable to get smooth scroll (C#)

We need a news ticker that scrolls text on a custom device running on Win CE. We developed a control with the help of string manipulations and displaying the text on Label. It's working, but the scrolling is not smooth.

At times it jumps few chars and at times cuts off before completing the scroll. Are they are any controls we can buy or any open source code that we can adopt?

Updated with code

while (true)
        {
            //
            if (stringBuilder.Length == 0)
                stringBuilder.Append(OrignalFullMessage);
            //
            stringBuilder = stringBuilder.Remove(0, 1);
            if (stringBuilder.Length > MAX_DISPLAY_CHARS)
            {
                eventArgs.Message = stringBuilder.ToString(0, MAX_DISPLAY_CHARS);

            }
            else
            {
                eventArgs.Message = stringBuilder.ToString().PadRight(MAX_DISPLAY_CHARS,' ');
            }

            //

            this.scrollerLabel.Invoke(new ScrollMessageUpdateHandler(WorkerUpdateHandler), this, eventArgs);
            Thread.Sleep(MESSAGE_SCROLL_DELAY);
            if (KillThread == true)
                return;

        }

We'd really need to see your scrolling and painting code, but my guess is that you're being both inefficient in your drawing and probably creating new strings with every draw operation, which is probably causing the GC to collect more frequently than is necessary.

Update1

Yes, your code for generating the scroll text is building a whole lot of unnecessary strings. Not only is this going to make the GC very busy, it's a whole lot of unnecessary string manipulation. There are at least a couple of ways you could approach this to improve things.

First you could keep one string of the text to be drawn and then keep offsets and numbers and draw using SubString - that would decrease the amount of string objects you're creating a lot .

Second, you could draw the whole string to a Bitmap and just slide the bitmap during the scroll operation.

using System;
using System.Drawing;
using System.Windows.Forms;
namespace MissingLink.Windows.Forms
{
public partial class ScrollingLabel : System.Windows.Forms.Control
{
    private static readonly StringFormat _strFmt = new StringFormat(StringFormatFlags.NoWrap);
    private float _txtWidth;
    private bool _needsScrolling;
    private float _x;
    private float _maxX;
    private const int _whiteSpaceLength = 40;
    private const int _updateTime = 1000;
    private const int _updateX = 5;
    private static readonly System.Threading.Timer _updateTimer = new System.Threading.Timer(ScrollText, 0, 0, 0);

    private static readonly object _updateTimerLock = new object();
    private StringFormat _nonScrollStrFmt = new StringFormat(StringFormatFlags.NoWrap);

    private static event EventHandler _updateTimerTick;

    private static event EventHandler UpdateTimerTick
    {
        add
        {
            lock(_updateTimerLock)
            {
                bool wasNull = (_updateTimerTick == null);
                _updateTimerTick += value;
                if(wasNull && (_updateTimerTick != null))
                {
                    _updateTimer.Change(0, _updateTime);
                }
            }
        }
        remove
        {
            lock(_updateTimerLock)
            {
                bool wasNull = (_updateTimerTick == null);
                _updateTimerTick -= value;
                if(wasNull && (_updateTimerTick == null))
                {
                    _updateTimer.Change(0, 0);
                }
            }
        }
    }

    public ScrollingLabel()
    {
        InitializeComponent();
    }

    private bool NeedsScrolling
    {
        get
        {
            return _needsScrolling;
        }
        set
        {
            if(_needsScrolling == value)
            {
                return;
            }
            UpdateTimerTick -= UpdateText;
            _needsScrolling = value;
            if(_needsScrolling)
            {
                UpdateTimerTick += UpdateText;
            }
        }
    }

    public StringAlignment TextAlign
    {
        get
        {
            return _nonScrollStrFmt.Alignment;
        }
        set
        {
            if(_nonScrollStrFmt.Alignment == value)
            {
                return;
            }
            _nonScrollStrFmt.Alignment = value;
            Invalidate();
        }
    }

    private void UpdateText(object sender, EventArgs e)
    {
        if(!NeedsScrolling)
        {
            return;
        }
        try
        {
            BeginInvoke((Action)(delegate
            {
                if(IsDisposed)
                {
                    return;
                }
                Invalidate();
            }));
        }
        catch { }
    }

    private static void ScrollText(object state)
    {
        EventHandler listeners = _updateTimerTick;
        if(listeners != null)
        {
            listeners(null, EventArgs.Empty);
        }
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        e.Graphics.Clear(BackColor);
        using(SolidBrush brush = new SolidBrush(ForeColor))
        {
            if(NeedsScrolling)
            {
                e.Graphics.DrawString(Text, Font, brush, _x, 0, _strFmt);
                e.Graphics.DrawString(Text, Font, brush, _x + _txtWidth, 0, _strFmt);
                _x -= _updateX;
                if(_x <= _maxX)
                {
                    _x = 0;
                }
            }
            else
            {
                e.Graphics.DrawString(Text, Font, brush, ClientRectangle, _nonScrollStrFmt);
            }
        }
        base.OnPaint(e);
    }

    private void UpdateNeedsScrollingFlag()
    {
        using(Graphics graphics = CreateGraphics())
        {
            float txtWidth = graphics.MeasureString(Text, Font).Width;
            if(txtWidth > Width)
            {
                NeedsScrolling = true;
                _txtWidth = txtWidth + _whiteSpaceLength;
                _maxX = _txtWidth * -1;
            }
            else
            {
                NeedsScrolling = false;
            }
        }
        _x = 0;
    }

    protected override void OnResize(EventArgs e)
    {
        UpdateNeedsScrollingFlag();
        Invalidate();

        base.OnResize(e);
    }

    protected override void OnTextChanged(EventArgs e)
    {
        UpdateNeedsScrollingFlag();
        Invalidate();

        base.OnTextChanged(e);
    }
}

}

ex:

Form1 f = new Form1();
  ScrollingLabel sl = new ScrollingLabel();
  sl.Text = @"qwertyuiopasdfghjklzxcvbnm1234567890QWERTYUIOPASDFGHJKLZXCVBNM!@#$%^&*()_+-={}[]|\:;'<,>.sl?/";
  sl.Dock = DockStyle.Fill;
  f.Size = new Size(100, 100);
  f.Controls.Add(sl);
  Application.Run(f);

you should print the text yourself on an owner drawn control. You can then just print the text at an X position which is decremented on every timer tick instead of relying on string manipulations.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM