Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scrolling Label in C#

I am looking for an efficient way of scrolling text like a marquee in web terminology.

I managed to achieve this using a piece of code I found online:

private int xPos = 0, YPos = 0;

private void Form1_Load(object sender, EventArgs e)
{
    //link label
    lblText.Text = "Hello this is marquee text";
    xPos = lblText.Location.X;
    YPos = lblText.Location.Y;
    timer1.Start();
}


private void timer1_Tick(object sender, EventArgs e)
{
    if (xPos == 0)
    {

        this.lblText.Location = new System.Drawing.Point(this.Width, YPos);
        xPos = this.Width;
    }
    else
    {
        this.lblText.Location = new System.Drawing.Point(xPos, YPos);
        xPos -= 2;
    }
}

The code is very simple and it uses, a timer tick event.

It works great initially, but after scrolling 3 or 4 times, it does not reappear.

Is there anything I can adjust to make the scrolling infinite?

like image 397
Cool Do Avatar asked Sep 22 '14 16:09

Cool Do


2 Answers

try:

private void timer1_Tick(object sender, EventArgs e)
{
    if (xPos <= 0) xPos = this.Width;
    this.lblText.Location = new System.Drawing.Point(xPos, YPos);
    xPos -= 2;
}

You mentioned that it 'cuts off' if the string is longer than the form width. I assume you mean that the label jumps back to the right side of the form as soon as it hits the left side, which means you can't read the full text?

If so, you could set the 'minimum Left' of the Label to be the negative of it's width. This would allow the label to scroll fully off the form before resetting it:

private void timer1_Tick(object sender, EventArgs e)
{
    // Let the label scroll all the way off the form
    int minLeft = this.lblText.Width * -1;

    if (xPos <= minLeft) xPos = this.Width;
    this.lblText.Location = new Point(xPos, yPos);
    xPos -= 2;
}

Or, you can set the 'minimum Left' to be the negative of the difference between the label width and the form width, so that it would not reset until the rightmost characters have been shown:

private void timer1_Tick(object sender, EventArgs e)
{
    // Ensure that the label doesn't reset until you can read the whole thing:
    int minLeft = (lblText.Width > this.Width) ? this.Width - lblText.Width : 0;

    if (xPos <= minLeft) xPos = this.Width;
    this.lblText.Location = new Point(xPos, yPos);
    xPos -= 2;
}

Many other options, too. Like having multiple labels running back to back in rotation, so there is never any blank text!! You can figure out how many labels to generate dynamically (based on the difference between their width and the form's width) and handle their positions in the Timer event.

like image 111
Rufus L Avatar answered Sep 26 '22 05:09

Rufus L


If you fill the Label by adding blanks til it has the length you want, this may look nicer:

private void timer1_Tick(object sender, EventArgs e)
{
   lblText.Text= 
   lblText.Text.Substring(1) + lblText.Text.Substring(0,1);
}

Adding the right amount of blanks may be a bit of a challenge, though. You will need to do that on Form.Resize! Doing this right is a bit trickier, than one might think.

The perfect marquee would combine the pixelwise movement and the rollover effect, maybe by owner-drawing the label, maybe like this 'Real Marquee', so 80s ;-)

class Marquee : Label
{
    public Timer MarqueeTimer { get; set; }
    public int Speed { get; set; }
    public int yOffset { get; set; }

    public void Start() { MarqueeTimer.Start(); }
    public void Stop()  { MarqueeTimer.Stop(); }

    private int offset; 
    SolidBrush backBrush ;
    SolidBrush textBrush ;

    public Marquee()
    {
        textBrush = new SolidBrush(this.ForeColor);
        backBrush = new SolidBrush(this.BackColor);
        yOffset = 0;
        Speed = 1;
        MarqueeTimer = new Timer();
        MarqueeTimer.Interval = 25;
        MarqueeTimer.Enabled = true;
        MarqueeTimer.Tick += (aSender, eArgs) =>
        {
            offset = (offset - Speed);
            if (offset < -this.ClientSize.Width) offset = 0;
            this.Invalidate();
        };
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);
        e.Graphics.FillRectangle(backBrush, e.ClipRectangle);
        e.Graphics.DrawString(this.Text, this.Font, textBrush, offset, yOffset);
        e.Graphics.DrawString(this.Text, this.Font, textBrush, 
                              this.ClientSize.Width + offset, yOffset);
    }
}

Looks really smooth and needs no outside code but Start, Stop and setting Speed and perhaps the MarqueeTimer Intervall. All regular Properties will work from the Designer, just set AutoSize=false and make it large enough to fill the area!

like image 42
TaW Avatar answered Sep 26 '22 05:09

TaW