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

Scrolling a Rich/Textbox Automatically

By , 1 Feb 2008
 

Introduction

I’ve been faced with a problem of automatically scrolling a richtextbox. For example: scrolling one pixel every 40 milliseconds.

Using C# under Visual Studio 2005, I’ve dropped a new richtextbox onto my form and expected to see a method similar to scroll(int pixels) under the richtextbox’s available methods.

Using intellisense, we get the following relevant options: ScrollToCaret().

ScrollCaret() will scroll our textbox to a given text, surely that doesn't satisfy our needs.
Ok, so what else have we got? Surprisingly that is all.

Background

So how do we achieve a smooth auto scrolling effect?
We're going to use the Win32 API SendMessage method to send a scroll message to our textbox asking it to scroll itself in pixels.

In case you are not familiar with messages, I strongly recommend reading the following explanation: message_loop.

So How Does a Scroll Message Look Like?

Using Microsoft Spy ++ we are going to view all scroll messages sent to our textbox.
First, launch your ScrollTextBox application which should be a form with a rich/textbox with some text in it. This will give you a scroller to play with.
Then run Spy++ and click the log message icon (ctrl + m).

Drag and drop the scope onto your textbox, next click the Messages tab and select only the Scrollbar option under the Message groups, Click OK.
A new dialog should appear: “Massages (Window 000206B6) though you will probably get a different window number.

Now scroll your textbox using the up/down arrows, or dragging the scroller and watch Spy logs all scroll messages received by the textbox.

scrollMsg.JPG

A Typical Scroll Message

<0007>000206B6 S WM_VSCROLL nScrollCode:SB_THUMBTRACK nPos:16 hwndScrollBar:null) 
  • <0007> is just the logger line number
  • WM_VSCROLL is the type of the message, in our case a Vertical scroll message
  • SB_THUMBTRACK is the first parameter of the message.
    The first message parameter tells us what type of scroll is performed.
    In the example I have given, I'm dragging the scroller.

A full list of first parameter values is given below:

  • nPos specifies the position to scroll to, It is part of the first parameter and is available only for SB_THUMBPOSITION or SB_THUMBTRACK scrolling.
  • hwndScrollBar is the second parameter of the message and is always null.

Here is a list of low-order words of wParam values to a WM_VSCROLL message:

  • SB_LINEUP
  • SB_LINEDOWN
  • SB_THUMBPOSITION
  • SB_THUMBTRACK
  • SB_TOP
  • SB_BOTTOM
  • SB_ENDSCROLL
  • SB_PAGEDOWN
  • SB_PAGEUP

What our simple application will do is send a WM_VSCROLL message to the textbox using the SB_THUMBTRACK parameter. It will set the high-order word of wParam to the desired position we want the scroller to be at.

Importing user32.dll

In order to send messages, we'll need to import the sendmessage method from the Win32 API:

[DllImport("User32.dll", CharSet = CharSet.Auto, EntryPoint = "SendMessage")]
        protected static extern IntPtr SendMessage
	(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

Now we can send messages to any handle we get our hands on.

int SB_LINEUP = 0; 
ptrWparam = new IntPtr(SB_LINEUP);
ptrLparam = new IntPtr(0);
SendMessage(txtArea.Handle, WM_VSCROLL, ptrWparam, ptrLparam); 

The above code will scroll txtArea one line up. To scroll a textbox by n pixels value, we'll need to do a little bit more work.

What we need is a way to determine where our scroller is at the moment. Then we could increase its position by a pixel value and all that's left is to send a new SB_THUMBTRACK message with the new position back to the textbox. Using the Win32 API GetScrollInfo method, we can get all kinds of information on a scroller bar:

GetScrollInfo(IntPtr hwnd, int fnBar, ref SCROLLINFO lpsi);  
  • Hwnd is an handle to our textbox.
  • fnBar is the size of the structure SCROLLINFO.
  • lpsi is a structure that will hold information about the scroller.

The SCROLLINFO structure:

struct SCROLLINFO
{
    public uint cbSize;
    public uint fMask;
    public int nMin;
    public int nMax;
    public uint nPage;
    public int nPos;
    public int nTrackPos;
} 

For more information about the structure, visit MSDN.

What we'll need is the nPos variable which will hold the current scroller position. So our basic scroll method will do something like this:

// Scrolls a given textbox. handle: an handle to our textbox. pixles: 
// number of pixels to scroll.
void scroll(IntPtr handle, int pixles)
{            
    // Get current scroller position
            
    SCROLLINFO si = new SCROLLINFO();
    si.cbSize = (uint)Marshal.SizeOf(si);
    si.fMask = (uint)ScrollInfoMask.SIF_ALL;
    GetScrollInfo(handle, (int)ScrollBarDirection.SB_VERT, ref si);
            
    // Increase position by pixles
    si.nPos += pixles;
                        
    // Reposition scroller
    SetScrollInfo(handle, (int)ScrollBarDirection.SB_VERT, ref si, true);

    // Send a WM_VSCROLL scroll message using SB_THUMBTRACK as wParam
    // SB_THUMBTRACK: low-order word of wParam, si.nPos high-order word of  wParam

    IntPtr ptrWparam = new IntPtr(SB_THUMBTRACK + 0x10000 * si.nPos);
    IntPtr ptrLparam = new IntPtr(0);
        SendMessage(handle, WM_VSCROLL, ptrWparam, ptrLparam);                
} 

That's it now to get the filling that the textbox is scrolling itself smoothly. Use a timer to call the above method say every 40 milliseconds and increase the scroller position by 1 pixel each time.

Ending

Surprisingly this simple "feature" is not built into the textbox functionality.
I've spent some time looking for a quick solution for my problem, but couldn't find one.
So I've decided to post my solution. I hope it will come in handy, In case you have any questions or comments, please feel free to post or you could email me.

History

  • 1st February, 2008: Initial post

License

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

About the Author

Lipman Roi
Software Developer ONE
Israel Israel
Member
2005 - 2006 Tel Aviv university
2006 - 2007 RoboGroup
Been involved in a number of E learning projects.
 
2007 - 2008 ONE
 


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   
QuestionSolved: Experimenting changing colors, scroll stops at each last passed line instead of current position when paused? [modified]memberDarkTyranno4 Mar '13 - 3:25 
Hi, I'm still updating my application and I've added some code that changes the font color while scrolling.
This has to be simultaneous with scrolling, so update color and move 1 pixel up, otherwise it will flicker.
So far it worked. Of course, when I pause the timer, the colors will stop changing as well.
I've tried to fix it this way:
- Pressing 'P' will cause Scrolltimer to be disabled.
- It will enable an extra 'pausetimer', which only continues changing color with getCurrentInterval and getCurrentColor.
When I press P, the colors do continue at the right speed, but the scroller jumps back to the last passed line instead of stopping at current position, it looks like it won't move using pixels anymore when I pause it.
 
When I disable my pausetimer (so colors won't change after pause) then it does work. The text remains at the right position when I pause and it keeps the current color. How can I solve this, that the colors continue to shine but without the text jumping back to last completely passed line?
Any ideas will be welcome, thanks!
 
Edit: using IRC online some people helped me out. It was quite simple and will be explained in my NFO article.

modified 3 May '13 - 8:26.

SuggestionCaution about using WM_VSCROLL!memberHillBillyBoy20 Feb '13 - 12:13 
Good article. But... WM_VSCROLL must not be used with SB_THUMBPOSITION unless you are sure your code will only be used to scroll short documents. That's because the position value used with SB_THUMBPOSITION is limited to 16 bits, which is only good enough for a scroll range of maybe a few dozen pages (depending on zoom level, dpi, etc). Use message EM_SETSCROLLPOS instead.
GeneralRe: Caution about using WM_VSCROLL!memberLipman Roi22 Feb '13 - 1:11 
Hi, Thank you for the comment,
When i developed the code above i was managing a small number of text pages but thanks again for the info.
GeneralRe: Caution about using WM_VSCROLL!groupvlad7812 Mar '13 - 14:05 
Hey there, you have seemed to have solved my problem to what I have been dealing with for a very long time. However, I am unsure of how to implement that with this code. Could you please provide an example?
MySignature.BringToFront();

NewsThanks! Changed code for use in own project.memberDarkTyranno12 Feb '13 - 3:50 
Thank you for this code, it was exactly what I needed. After I checked your demo program, I saw it stopped if it reached the end. I wanted to loop it. After trying to understand how the code works, I changed the code the way I wanted it. I also made buttons/keypresses available that can change the speed of the interval per 1 pixel. For my app it works excellent. I also explained how I implemented this and reffered to this page as the source, great job! Big Grin | :-D
 
For those interested, here's the function in my application:
Creating a NFO Viewer in C# as a beginner[^]
GeneralRe: Thanks! Changed code for use in own project.memberLipman Roi12 Feb '13 - 8:01 
Hi, I'm really glad to know you've find my code useful! knowing that you've managed to incorporate it into your own project is just awesome!.
 
thanks for the credit..
QuestionThis is works for small number of text.groupvlad7817 Jun '12 - 14:33 
I have been developing a software for a while now that uses this same technique to scroll. However I have run into a problem. You see, when you scroll, you are scrolling the scrollbar itself, rather than the textbox view. And for a 1000 character speech. This is fine. The scrolling is fairly smooth, and the rate is steady as long as you do x pixels per 1 millisecond. But when you have something very, very lengthy, such as war and peace, Scrolling by 1 pixel (of the scrollbar itself) will scroll very many lines of text at once. whereas scrolling 1 pixel in a 1000 character speech does the job just fine. My question is: Is there a way, or at least, have you figured out how to scroll by x amount of pixels of the...lets call it scrollview?
 
Thank you, and this will really help me out here, as my boss is annoyed with the approach I have taken.
 
Thank you in advance, once again, hope to hear from you soon.
MySignature.BringToFront();

AnswerRe: This is works for small number of text.memberLipman Roi7 Jun '12 - 20:10 
Hi vlad, i see what you'r saying, indeed when you've got a large piece text scrolling by clicking the scroll arrows might not even change the scroller's position.
 
Two immediate "solutions" comes to my mind right away.
 
1.Send click messages to the up/down/left/right scroll arrow buttons instead of the scroller.
 
2.Come up with a clever solution to contain only part of your entire text at every given time, such that scrolling by one pixel will have the desired effect, swapping in and out parts of the text which are not visible.
 
I hope I've managed to help out.
Generalmajor help needed!membervlad78127 Jan '11 - 13:10 
what you did is exactly what i need. but i do not understand how to insert these codes into my project. Frown | :(
one of the main problems is the part with marshal in it. i get about 12 errors when i past the code in. do you mind telling me exactly where to put in each code?
thanks in advance and i hope you reply soon. the project im working on needs to be finished in less than a month and this is the main problem!
Vlad781

GeneralRe: major help needed!memberLipman Roi27 Jan '11 - 22:50 
Hi Vlad, I'm glad you find my post useful, you can have a look at the demo project I've included at the top of the article as an example to using the code.
If you're still having difficulties please provide you're email and i'll contact you directly,
Good Luck Lippman Roi.
GeneralRe: major help needed! [modified]membervlad78128 Jan '11 - 4:28 
yes, i have looked at the demo project its what i need. but instead of scrolling right when the application opens, i have an up and down button. i do not understand the part about user32.dll where to insert those functions into the script. you may email me at: karatevlad AT gmail Dot com
thanks for your reply.
modified on Friday, January 28, 2011 5:52 PM

GeneralThank YoumemberMember 740954115 Nov '10 - 3:51 
Thank you so much, I've been looking for the High/LowWord-Sending for hours!
Questionhow do you change direction?membercheborneck22 Mar '10 - 0:19 
i like this technique. i've played around with it but haven't had success changing direction. i'd like to make it scroll in either direction. any thoughts? thanks, tom
AnswerRe: how do you change direction?memberLipman Roi22 Mar '10 - 8:04 
To reverse scrolling direction change: the scroll method as such
void scroll(IntPtr handle, int pixels)
{
pixels *= -1; // scrolls upwards.
}
GeneralRe: how do you change direction?membercheborneck22 Mar '10 - 9:16 
hi, thanks for the response.
 
that doesn't fix my problem, let me explain.
 
i'm actually trying to control the scrolling of one listview control from another.
 
the listviews are in detail view mode and i have extended them to raise a scroll event. not using a textbox like in your example. when i receive the event from one listview, si.nPos actually returns an index and not the number of pixels. so the index goes up and down from zero depending on direction. never negative.
 
passing si along to setscrollinfo works fine. no matter what direction you scroll, that bar follows nicelly. it's when you send the WM_VSCROLL message that the contents only scroll in one direction. even if you negate the pos before or'ing the tag.
 
i am trying to hack what you're doing with a textbox and may not work for a listview but i've been looking for something that'll work.
 
i can hook WM_VSCROLL in WndProc and just pass it along to the other listview without creating an event and it will scroll the contents properly but only using the scroll buttons and not using the trackbar.
 
so i guess the question really should be how is wParam formatted to switch directions. or is this even the right method of doing this?
 
your expertise is hreatly appreciated.
 
regards, tom
QuestionPlease help take a look at this issuememberzystory16 Dec '09 - 16:12 
When the contents of the text scrolling speed will soon be too large, may I ask how to adjust the speed based on content, thank you
AnswerRe: Please help take a look at this issuememberLipman Roi16 Dec '09 - 19:50 
Are you trying to adjust the speed of scrolling according to the length of text in the textbox? if so
you could define a relation between the two and change the timer interval or change the scrolling amount foreach tick, hope i answered you're question.
GeneralRe: Please help take a look at this issuememberzystory17 Dec '09 - 19:50 
thank.
 
When the content is very large after the timer interval is set scrolling speed can be controlled, but not very smooth
GeneralRefresh ContentmemberMember 197994811 Dec '09 - 1:18 
Hi,
the scrolling function work perfectly but i have aproblem with the content of my ListView, because i update values inside the listview.
 
this is my code:
 
//Get the current position
SCROLLINFO si = new SCROLLINFO();
si.cbSize = (uint)Marshal.SizeOf(si);
si.fMask = (uint)ScrollInfoMask.SIF_ALL;
GetScrollInfo(this.telemetriaView.Handle, (int)ScrollBarDirection.SB_VERT, ref si);
 
//Update ListView Content
this.ListView.Items.Clear();
this.ListView.BeginUpdate();
.....
this.ListView.EndUpdate();
 
//Set a previous position of a scrollBar
SetScroll(this.ListView.Handle, si.nPos); //this work with scrollbar but not to ListView content.
Thanks
 
Regards
ALO
Generalcorner casememberspelger30 Sep '09 - 6:08 
there is a corner case that does not work. if the horz scroll bar is visible then scrolling to the bottom does not really scroll to the bottm...
GeneralRe: corner casememberLipman Roi30 Sep '09 - 6:12 
Thank you for pointing this out.
GeneralRe: corner casememberspelger30 Sep '09 - 6:34 
here is a solution i just found...
 
the following code gets the current scroll info,
then sets the position to the max,
then sets the new scroll info,
then issues a line down. the extra line down causes the scroll bar to draw properly, try it without it and you'll see what i mean.
 
Handle == the handle of the rich text box
 
SCROLLINFO si = new SCROLLINFO();
si.cbSize = (uint)Marshal.SizeOf(si);
si.fMask = (uint)ScrollInfoMask.SIF_ALL;
GetScrollInfo(Handle, (int)ScrollBarDirection.SB_VERT, ref si);
 
si.nPos = si.nMax;
SetScrollInfo(Handle, (int)ScrollBarDirection.SB_VERT, ref si, false);
SendMessage(Handle, WM_VSCROLL, new IntPtr(SB_LINEDOWN), new IntPtr(0));

GeneralPerfect!memberVin552 Sep '09 - 7:15 
Thanks man, this saved my life, you're a genious. I've been looking for this for 2 weeks now and finally i found it, can't tell how happy i am. All i wanted to say is a big THANK YOU!
 
Vincent
GeneralRe: Perfect!memberLipman Roi2 Sep '09 - 9:30 
you're welcome
QuestionVB.Net Version??memberFastBurst16 Feb '09 - 10:01 
Do you have a VB.Net version, converters were no luck too many errors.

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

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130523.1 | Last Updated 1 Feb 2008
Article Copyright 2008 by Lipman Roi
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid