Click here to Skip to main content
Email Password   helpLost your password?

Scroller
CScrollerCtrl test app, running under Windows XP.

Contents

Motivation:

Well... I was bored the other day, and not wanting to do anything necessary, decided to write a little autoscroller. It turned out quite nicely, so I'm posting it here in the off chance it might be useful to someone else.

Description:

An autoscroller is a control that displays text, scrolling it automatically at a pre-determined speed. They are generally used for displaying credits in about dialogs, or other such trivial tasks; by design they are not friendly enough to be used for displaying important things, since users will be frustrated if they read faster or slower than the text is scrolled. My scroller has an optional scrollbar though, just to make it easier for such users should it ever be used to display text they will actually want to read.

Features:

Use:

The public API for CScrollerCtrl consists of the following methods:

// create the window; remove WS_VSCROLL to avoid showing scrollbar, 

// remove WS_TABSTOP to disable keyboard scrolling.

BOOL Create(const RECT& rect, CWnd* pParentWnd, 
UINT uStyle = WS_CHILD|WS_VISIBLE|WS_VSCROLL|
  WS_TABSTOP|WS_GROUP, UINT nID = 0);
// activate/deactivate wrapping mode: 

void SetWrapping(BOOL bWrap); 
// Sets the color used for the background (if no pattern is set) or margins 

// (if pattern is set and not tiled) 

void SetBgColor(COLORREF clrBg); 
// Sets the color used for text 

void SetFgColor(COLORREF clrBg); 
// Sets the font; size is in points,

// see LOGFONT documentation for weight constants 

void SetFont(const CString& strName, int nSize, int nWeight); 
// Sets the text to be displayed 

void SetText(const CString& strText); 
// Sets the bitmap to be displayed above the text 

CBitmap* SetLogo(CBitmap* pbmpLogo); 
// Sets the background pattern 

CBitmap* SetPattern(CBitmap* pbmpPattern, BOOL bTile); 
// Sets the time (in milliseconds) between frames (autoscrolling speed) 

// (will revert to default if less than 0)

void SetScrollDelay(int nScrollDelay); 
// Sets the delay (in milliseconds) when autoscrolling pauses 

// (will disable pausing if set less than scroll delay)

void SetScrollPause(int nScrollPause); 

Calling Create() gives the control its initial size and position, as well as the styles in effect. To disable the scroll bar, remove the WS_VSCROLL style; to disable all manual scrolling, remove both WS_VSCROLL and WS_TABSTOP.

All other methods can be called either before or after the control is created; if the control has been created, they will take effect immediately, otherwise they will be in effect when Create() is called. All attributes have defaults, so you only need to set the ones you need. Most are self-explanatory, but a couple of things should be known:

The Demo:

The demo consists of a main dialog and child dialog. The main dialog displays the introduction above, along with a logo bitmap, in an auto-scrolling window with a scrollbar. The child dialog illustrates changing the text on the fly by presenting a scrolling tips window.

The demo is mostly autogenerated, so you can safely ignore most of the code. The important bits are in CScrollerTestDlg::OnInitDialog(), CTipsDialog::OnInitDialog(), and CTipsDialog::SwitchTip().

CScrollerTestDlg::OnInitDialog():

Here i begin by initializing the scroller with one of two sets of background patterns and colors:

   if ( ::GetTickCount()%2 )  // lazy man's rand()

   {
      m_scroller.SetPattern(CBitmap::FromHandle(
          (HBITMAP)::LoadImage(AfxGetResourceHandle(), 
MAKEINTRESOURCE(IDB_BACKGROUND), 
IMAGE_BITMAP, 0,0, LR_SHARED)), FALSE);
      // this background should be centered over a white background

      m_scroller.SetBgColor(RGB(255,255,255));
      m_scroller.SetFgColor(RGB(0,127,0));
   }
   else
   {
      m_scroller.SetPattern(CBitmap::FromHandle(
          (HBITMAP)::LoadImage(AfxGetResourceHandle(), 
MAKEINTRESOURCE(IDB_BACKGROUND2), 
IMAGE_BITMAP, 0,0, LR_SHARED)), TRUE);
      m_scroller.SetFgColor(RGB(255,255,225));
   }

Note that in the first case, the bitmap is centered, while in the second it is tiled.

Next, I set the logo bitmap and text:

   // logo bitmap

   m_scroller.SetLogo(CBitmap::FromHandle(
      (HBITMAP)::LoadImage(AfxGetResourceHandle(), 
MAKEINTRESOURCE(IDB_SCROLLER), 
IMAGE_BITMAP, 0,0, LR_SHARED)));
   // text

   CString strIntro;
   strIntro.LoadString(IDS_INTRO);
   m_scroller.SetText(strIntro);

Finally, I create the scroller to fill the entire client are:

    CRect rect;
    GetClientRect(&rect);
    m_scroller.Create(rect, this);

It's probably worth noting here that this dialog is resizeable, and that the scroller is resized in OnSize() to always fill the client area. I set the WS_CLIPCHILDREN style for the dialog to prevent flashing during resizing.

After creating the scroller, I create and show the tips dialog:

   // create tips dialog, and show at top-left of screen

   m_dlgTips.Create(CTipsDialog::IDD, this);
   m_dlgTips.SetWindowPos(NULL,0, 0, 0,0, SWP_NOZORDER
       |SWP_NOSIZE|SWP_NOACTIVATE|SWP_SHOWWINDOW);

CTipsDialog::OnInitDialog():

Here I begin by initializing the scroller with a custom font, a message, and turning off wrapping. I'll explain the reason for turning off wrapping shortly:

   m_scroller.SetFont("Microsoft Sans Serif", 10, FW_SEMIBOLD);
   m_scroller.SetText("\tHello!");
   m_scroller.SetWrapping(FALSE);

The messages displayed in this scroller aren't long enough that they need to be readable while scrolling, so I let them scroll as fast as possible, pausing when the tip is fully shown.

   // short messages, read while paused, not scrolling

   // so scroll quickly and pause for a 6 secs.

   m_scroller.SetScrollDelay(0);
   m_scroller.SetScrollPause(6000);

Finally do the creation. Note that I give the control an ID (1) and omit the WS_VSCROLL and WS_TABSTOP styles; I don't want the user to be able to scroll this manually.

   // short messages, read while paused, not scrolling

   // so scroll quickly and pause for a 6 secs.

   m_scroller.SetScrollDelay(0);
   m_scroller.SetScrollPause(6000);

Ok, now an explanation for turning off wrapping mode. The purpose of this dialog is to display tips, one after another, and to do this by changing the text at appropriate times. If wrapping is turned on, the text will never be completely off screen, and switching will not look good. Now that that's clear, on to how the text is actually switched...

CTipsDialog::SwitchTip():

CScrollerCtrl is an output only control; the only user input it accepts is manual scrolling, and this it handles internally. However, it does send one command message to its parent when the content has scrolled off the screen completely. There is a constant defined (CScrollerCtrl::SC_SCROLL_COMPLETE) to identify this command message, but since it is the only command message sent by this control, it is not really necessary to check this. SwitchTip() handles the command message when it is received, and changes the text. The new text then scrolls on screen, and life goes on.

Implementation:

This is a fairly simple control... Output is double-buffered to ensure smooth updates. Two timers are used: the first is active for the life of the window, ticking off at the scroll rate. The second is used to clear the paused state when autoscrolling is paused for whatever reason; it is killed as soon as it is triggered. All drawing is contained in one of three methods:

These methods are all virtual, so if you create a derived class, you can override any or all of them to do something interesting. (display rich text, etc.)

Compatibility:

I've tested this on Windows XP, 2000, and 98. It will compile with both Visual Studio 6 and Visual Studio .NET

TODO:

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralCan we scroll Text Hrizontally?
Digvijay Singh Chauhan
3:56 23 Mar '05  
Hi,

This is nice!!!

But can i get the text scrolling horzontally say left to right or right to left.
Don't need the code but need to know here to do it.

Thanks in advance,

Digvijay

~Smile
GeneralI found a bug
swandream
17:50 27 May '03  
If you use this control on a dialog bar, show or hide the dialog bar will pop up an error message about assert failure,I have a test about this, but I don't know how to post it, I hope Shog9 can solve this problem.
gengzw@163.com

swandream
GeneralRe: I found a bug
Shog9
18:23 27 May '03  
I've not seen this - could you post the code you use to create and show/hide the dialog bar and scroller?

- Shog9 -
I'd show a smile but I'm too weak
I'd share with you, could I only speak

GeneralRe: I found a bug
swandream
23:33 27 May '03  
I have mail the code to you, please check it out

swandream
GeneralRe: I found a bug
Shog9
6:57 28 May '03  
Ok, i've taken a look at your code now.

First, as you already noticed, one problem is caused by having the creation code in OnPaint(). The second problem is a bug in the scroller:

CScrollerCtrl::SetPattern() takes a pointer to a CBitmap object as one of it's parameters. Most MFC functions are wrappers for Win32 API calls and thus store only the bitmap handled contained in a CBitmap parameter. This routine however, stores a pointer to the object itself. All would be well, except in this bit of code:
m_scroller.SetPattern(CBitmap::FromHandle((HBITMAP)::LoadImage(AfxGetResourceHandle(), 
MAKEINTRESOURCE(IDB_BITMAP_RANGE), IMAGE_BITMAP, 0,0, LR_SHARED)), FALSE);
Here, you're passing in a temporary object, created by CBitmap::FromHandle(). These objects are automatically deleted by MFC in it's idle processing, and the pointers should never be stored.

Fortunately, the work-around for this is easy - just add a CBitmap member variable to your class, and use that:
m_bmpBackground.Attach((HBITMAP)::LoadImage(AfxGetResourceHandle(), 
MAKEINTRESOURCE(IDB_BITMAP_RANGE), IMAGE_BITMAP, 0,0, LR_SHARED));
m_scroller.SetPattern(&m_bmpBackground, FALSE);

Now, i should mention - i'm a bit confused by your design here. There are three windows where there really only needs to be one. If you wish to add a control to the dialog bar as a placeholder (which you did with IDC_RANGE), you should simply retrieve its area and then destroy it. You only need to do this once, not every time OnPaint() is called. Same with setting the text and background image, just do it once unless you actually are changing it - calling these every time OnPaint() is executed will just make it slow.

Anyway, add a CBitmap class member and use that right off, & that should take care of your most pressing problem.

- Shog9 -
I'd show a smile but I'm too weak
I'd share with you, could I only speak

GeneralRe: I found a bug
swandream
22:58 28 May '03  
It is really nice of you, thank you veeery much for your help, I am really touched.
best regards

swandream
GeneralRe: I found a bug
swandream
23:36 28 May '03  
I forgot, I should explain why my code made you confused, because I want to implement this effect, first create a rect, I set a bitmap as the background together with puting a title on the top of this rect, then I made a second rect, this rect will do the scrolling job, I don't want to the scrolling text erase the title,just like this
|-----------------|
| title |
| |------------| |
| | | |
| | Scrolling | |
| | | |
| |------------| |
|-----------------|
My main problem is how can I make the inner scrolling rect be transparent? by the way, I know place those scrolling code in OnPaint is a very bad choice, but I don't know where should I place,I'll mail the code when putting them in OnCreat() , it seems having some other errors.


swandream
GeneralRe: I found a bug
Shog9
18:05 29 May '03  
First off, you can't make this control transparent, at least not easily - it does all its drawing into a back buffer, and then repaints the entire window. There's no way to prevent parts from being redrawn.

You could modify the control to draw the background offset slightly, so it would match the background of the window it is in. Or you could arrange the controls in such a way that there is no seam between them.

Now, the creation code. Watch carefully:
First, add this member variable to your CMyDialogBar class:
CBitmap m_BackGround;
Then, change the code in OnCreate() to read as follows:
	BackGround.Attach((HBITMAP)::LoadImage(AfxGetResourceHandle(), 
MAKEINTRESOURCE(IDB_BITMAP_RANGE), IMAGE_BITMAP, 0,0, LR_SHARED));
m_scroller.SetPattern(&BackGround, FALSE);

// this background should be centered over a white background
m_scroller.SetBgColor(RGB(117,165,218));
m_scroller.SetFgColor(RGB(255,255,0));
CString strIntro;
strIntro.LoadString(IDS_INTRO);
m_scroller.SetText(strIntro);
m_scroller.Create(CRect(21,17,191,237), this);
Remove the line that creates the container control (or arrange the two so they don't overlap).

Good luck!

- Shog9 -
I'd show a smile but I'm too weak
I'd share with you, could I only speak

GeneralRe: I found a bug
swandream
5:12 30 May '03  
I have mailed the codes to you again, I don't know wether you have checked it out, there still has the problems, Assert failure and motion blur while scrolling up,please...

swandream
GeneralI have a question?
hk028
23:11 22 Jan '03  
Your program is very good!
But my environment is not same to you.
I have build a project with Dialog.Use the main dialog popup a dialog named seconddialog,then use the seconddialog create four no-model dialog ,but this four diaog cannot process srcoll event.
What can i do?
Thanks for your help!

hk028
GeneralRe: I have a question?
Shog9
9:56 23 Jan '03  
Not sure i understand what your problem is... which dialog has the scroll control in it? Confused

---
Shog9 The siren sings a lonely song - of all the wants and hungers
The lust of love a brute desire - the ledge of life goes under

GeneralI need help!
hk028
16:28 24 Jan '03  
I am sorry for not saying clearly!
My meant is:
I have made a main dialog which is a general dialog,and i put a button on it.When I click the button,it comes out a secondary dialog along with four no-model dialogs.I apply the class which is offered by you in these four no-model dialogs.The information is supposed to be scrolled, but there isnot any scrolls in these four dialog.
What can i do?
I need your help!thanks!

hk028
GeneralNice
Boris Sundic
18:12 16 May '02  
This is a real eye-candy!;) But, when I move mouse over then scrolling text, vertical scrollbar comes out. Is it a bug or not cause I just saw a demo. I hope I can solve that in code.
Now, get to article.Big Grin
GeneralRe: Nice
Shog9
19:07 16 May '02  
Heh, that actually is a feature. No, really, it is! But it can be disabled if desired. Smile

Boris Sundic wrote:
Now, get to article.
All will soon be apparent then. Big Grin

--------

This is what we've waited for
This is it boys, this is War!

--Shog9 --



Last Updated 10 Mar 2002 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010