This example shows how to access scrollbar events in a custom control that is derived from the
System.Windows.Forms.ListBox control, in C# .NET. Questions answered are:
- How do I get the Scroll event?
- How do I get the Scrollbar position?
Create a new project (File, New, Project...) of type 'Windows Application'. Visual Studio will show the project in your Solution Explorer and 'Form1.cs' in design mode. Right-click the project node and select 'Add, Add User Control...'. Name it 'ScrollingListBox.cs'. It is now visible in the Solution Explorer and it looks like a gray square in the designer.
Press F7 to go to the code and replace the base '
UserControl' by '
ListBox'. Save, compile, go to designer. As you can see, the icon in the Solution Explorer changes and the designer doesn't show a listbox. This is your code at this point:
public class ScrollingListBox : System.Windows.Forms.ListBox
private System.ComponentModel.Container components = null;
protected override void Dispose( bool disposing )
if( disposing )
if(components != null)
base.Dispose( disposing );
#region Component Designer generated code
private void InitializeComponent()
components = new System.ComponentModel.Container();
We are going to override the
WndProc and handle the scrollbar events. Here're the constants with the same name as in the C:\Program Files\Microsoft Visual Studio .NET\Vc7\PlatformSDK\Include\WinUser.h include file.
private const int WM_HSCROLL = 0x114;
private const int WM_VSCROLL = 0x115;
private const int SB_LINELEFT = 0;
private const int SB_LINERIGHT = 1;
private const int SB_PAGELEFT = 2;
private const int SB_PAGERIGHT = 3;
private const int SB_THUMBPOSITION = 4;
private const int SB_THUMBTRACK = 5;
private const int SB_LEFT = 6;
private const int SB_RIGHT = 7;
private const int SB_ENDSCROLL = 8;
private const int SIF_TRACKPOS = 0x10;
private const int SIF_RANGE = 0x1;
private const int SIF_POS = 0x4;
private const int SIF_PAGE = 0x2;
private const int SIF_ALL = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS;
When we recognize the scroll message in the
WndProc, we need to call
GetScrollInfo in user32.dll to find out the position. (The Microsoft documentation says that this position is sent in the
wparam of the scroll message when the type is
SB_THUMBTRACK, but that's simply not true.) So, first
GetScrollInfo is imported from the DLL.
[DllImport("user32.dll", SetLastError=true) ]
private static extern int GetScrollInfo(
IntPtr hWnd, int n, ref ScrollInfoStruct lpScrollInfo );
ScrollInfoStruct is called
SCROLLINFO in Win32 and it's the typical query construct you see in Microsoft APIs: an info structure is loaded with its own size and a question-code (and optionally arguments for the question). This be the case, the question is 'what's your position?', or '
private struct ScrollInfoStruct
public int cbSize;
public int fMask;
public int nMin;
public int nMax;
public int nPage;
public int nPos;
public int nTrackPos;
Finally, detect the message and fire the event
OK, now let's fire the standard
ScrollEvent when we receive a scroll message. For this, we need the event, which I called it '
Scrolled', since this is the naming that Microsoft uses for events (like '
public event ScrollEventHandler Scrolled = null;
WndProc, get the scroll message and fire the
Scrolled event. In this example, I only respond to the
WM_HSCROLL message, since that's the one needed for an owner drawn listbox control. The code for
WM_VSCROLL would be exactly the same, however. In this example, I'm only interested in the end-scroll message, which is fired after every scrolling action.
protected override void WndProc(ref System.Windows.Forms.Message msg)
if( msg.Msg == WM_HSCROLL )
if( Scrolled != null )
ScrollInfoStruct si = new ScrollInfoStruct();
si.fMask = SIF_ALL;
si.cbSize = Marshal.SizeOf(si);
GetScrollInfo(msg.HWnd, 0, ref si);
if( msg.WParam.ToInt32() == SB_ENDSCROLL )
ScrollEventArgs sargs = new ScrollEventArgs(
To be able to use the
Marshal class, you have to reference the
That should do it. You can extend it by capturing the vertical scrollbar or by capturing more scroll events. If you want to know the scrollbar position at any other time, use the
Also, I haven't tried it, but it probably works for any control with a scrollbar in it.
In case you're wondering, I needed this code to have other controls 'scroll along' with a