Click here to Skip to main content
Click here to Skip to main content

A marquee control in C#

By , 19 Feb 2003
 

Overview

This application demonstrates how to create a "string loop" in C#. A common use of "string loops" is in web advertisements, where the slogan or product name appears to be revolving in a circle. The code creates a rectangle in the middle of the client area that is just large enough to fit the entire message, minus any trailing white space. Clicking on the form or resizing it will cause the "string loop" to restart.

Points to Notice

Rather than use a string to represent the message ("Catch Me If You Can…" ) it is far more efficient and intuitive to use the StringBuilder class, found in the System.Text namespace. StringBuilder allows you to directly manipulate a string, rather than having to make copies of a regular string. The manipulation of the StringBuilder’s internal string is quite straightforward:

private void DrawMovingText()
{
    Graphics grfx = CreateGraphics();
    Font font = new Font( "Courier New", 20, FontStyle.Bold );
    string spaces = " ";
    // 
    // StringBuilder is used to allow for efficient manipulation of 
    // one string, rather than generating many separate strings
    //
    StringBuilder str = 
        new StringBuilder( "Catch Me If You Can..." + spaces );

    Rectangle rect = CreateRect( grfx, str, font );

    int numCycles = str.Length * 3 + 1;
    for( int i = 0; i < numCycles; ++i )
    {
        grfx.FillRectangle( Brushes.White, rect );
        grfx.DrawString( str.ToString(), font, Brushes.Red, rect );
        // relocate the first char to the end of the string
        str.Append( str[0] );
        str.Remove( 0, 1 );
        // pause for visual effect
        Thread.Sleep( 150 );
    }
    grfx.Dispose();
}

To avoid having DrawMovingText() eat up all of the CPU's attention it is placed on a worker thread. Every time the user clicks on the form or resizes it, the thread is killed and reborn. Obviously, if you wanted to alter the speed at which the letters "move" you would simply adjust the number of milliseconds that the thread sleeps for.

//
// All of these events will restart the thread that draws 
// the text
//
private void Form_Load( object sender, System.EventArgs e )
{
    StartThread();
}
private void Form_MouseUp(object sender, 
                          System.Windows.Forms.MouseEventArgs e)
{
    StartThread();
}
private void Form_Resize( object sender, System.EventArgs e )
{
    StartThread();
}
private void label_Click(object sender, System.EventArgs e)
{
    StartThread();
}
private void StartThread()
{
    thread.Abort();
    // give the thread time to die
    Thread.Sleep( 100 ); 
    Invalidate();
    thread = new Thread(new ThreadStart(DrawMovingText));
    thread.Start();
}

The final point of interest is how the size and location of the rectangle that contains the message are determined. The Graphics class has a MeasureString method that will give you the width or height of the string that you are passing in. Those are used for the width and height of the rectangle. You then use those values in tandem with the dimensions of the form to determine the coordinates of where the rectangle will originate from.

private Rectangle CreateRect
( Graphics grfx, StringBuilder str, Font font )
{
    // + 5 to allow last char to fit
    int w = (int)grfx.MeasureString(str.ToString(), font).Width + 5; 
    int h = (int)grfx.MeasureString( str.ToString(), font ).Height;
    int x = (int)this.Width / 2 - w / 2;
    int y = (int)this.Height / 2 - h;
    return new Rectangle( x, y, w, h );
}

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Josh Smith
Software Developer (Senior) Cynergy Systems
United States United States
Member
Josh creates software, for iOS and Windows.
 
He works at Cynergy Systems as a Senior Experience Developer.
 
Read his iOS Programming for .NET Developers[^] book to learn how to write iPhone and iPad apps by leveraging your existing .NET skills.
 
Use his Master WPF[^] app on your iPhone to sharpen your WPF skills on the go.
 
Check out his Advanced MVVM[^] book.
 
Visit his WPF blog[^] or stop by his iOS blog[^].

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 5membermanoj kumar choubey5 Mar '12 - 2:27 
NICE
GeneralMy vote of 1memberali32b21 Nov '10 - 4:30 
ugly
QuestionTransparent background of rectanglememberMember 273427022 Jun '09 - 18:45 
Hi Dear
 
I have set the background of rectagle to the Brushes.Tranparent color.
 
But the problem with this is while marqueeing the text overlaps.
 
How can I resolve the same problem while keeping the rectangle transparent?
 
Regards
Shobhit Jain
AnswerRe: Transparent background of rectanglememberrootjumper17 Sep '09 - 22:22 
Hi guy,
 
i made some changes in code of this control.. to use a thread to do this is not the best way overall..
 
use my code of drawing the text scrolling. i use a timer and u can set the background color to transparent or other.
 

public class ScrollingLabel : Label
{
public enum ScrollDirection{Left,Right};
private ScrollDirection _ScrollDirection = ScrollDirection.Left;
private Timer tScroll = new Timer();
private bool bActive;
int numCycles;
StringBuilder sb;

public override string Text {
get { return base.Text; }
set {
base.Text = value;
numCycles = (Text.Length * 3) + 1;
sb = new StringBuilder(this.Text + " ");
this.Invalidate();
}
}
public ScrollDirection ScrollingDirection {
get { return _ScrollDirection; }
set { _ScrollDirection = value; this.Invalidate(); }
}
public bool Active {
get { return bActive; }
set {
bActive = value;

if(value == false)
{
tScroll.Stop();
this.Invalidate();
}
else
tScroll.Start();
}
}
public int Interval{
get{return tScroll.Interval;}
set{tScroll.Interval = value; this.Invalidate();}
}

public ScrollingLabel()
{
SetStyle(ControlStyles.AllPaintingInWmPaint|
ControlStyles.DoubleBuffer|
ControlStyles.ResizeRedraw|
ControlStyles.UserPaint|
ControlStyles.SupportsTransparentBackColor,
true);

UpdateStyles();
tScroll.Tick += ScrollTick;
tScroll.Interval = 100;
this.Active = true;
}

private void ScrollTick(object sender, EventArgs e)
{
this.Invalidate();
if(_ScrollDirection == ScrollDirection.Left)
{
sb.Append(sb[0]);
sb.Remove(0,1);
}
else
{
sb.Insert(0,sb[sb.Length-1].ToString(),1);
sb.Remove(sb.Length-1,1);
}

}

protected override void OnPaint(PaintEventArgs e)
{
try
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.CompositingQuality = CompositingQuality.HighQuality;
e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;

Rectangle rect = CreateRect(e.Graphics,sb,this.Font);

e.Graphics.DrawString(sb.ToString(),this.Font,new SolidBrush(this.ForeColor),rect);

}catch
{

}
}

private Rectangle CreateRect( Graphics grfx, StringBuilder str, Font font )
{
// + 5 to allow last char to fit
int w = (int)grfx.MeasureString(str.ToString(), font).Width + 5;
int h = (int)grfx.MeasureString( str.ToString(), font ).Height;
int x = (int)this.Width / 2 - w / 2;
int y = (int)this.Height / 2 - h / 2;
return new Rectangle( x, y, w, h );
}
}

Generalthumbs upmembernelsonpaixao1 Aug '08 - 13:35 
Hi,
 
I was searching for something that works like that, thumbs up!!!
 
Thanks Big Grin | :-D
GeneralWidth of scrolling text.memberDivermarv27 Aug '04 - 12:44 
This is a great control. However, I need to have the with of the scroll be the entire width of the window. I know how to set this width, but when I do, the rectangle get sized, but the text still scrolls within a small space. Well, depending on the width determined from the string. Basically no matter how much text, I need it to scroll across the entire screen.
 
Any ideas on this?
 
Thanks
GeneralRe: Width of scrolling text.memberIcemanlaw30 Jan '05 - 4:34 
If you add lots more spaces on to the end of the Stringbuilder command, then it will think the string is longer than it is... Sorted! And so will fill up to the edge of the rectangle!
 
Chris
GeneralThread.JoinmemberThong Nguyen20 Feb '03 - 17:23 
Using Thread.Sleep(random_number) to wait for a thread to die is unreliable (and provably incorrect).
 
Use Thread.Join() instead.
 
Also, creating a new thread every time they resize the window will consume *lots* of CPU and OS resources. Why not simply use an AutoResetEvent and make the thread to wait?
GeneralRe: Thread.JoinmemberThong Nguyen20 Feb '03 - 17:26 
Just to add to that...you should only use Thread.Abort() as a last possibly resort.
 
Use a flag or other means to signal your thread to exit gracefully.
GeneralRe: Thread.JoinmemberMichael Potter21 Feb '03 - 2:53 
You also should not use a worker thread to update the UI. Only the UI thread can do that successfully (Windows API issue). Yes, you can get away with it for a while but, it will eventually fail.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130523.1 | Last Updated 20 Feb 2003
Article Copyright 2003 by Josh Smith
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid