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

Sample Image - ScrollingListbox.png

Introduction

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:

Getting Started

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:

using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;

namespace WebsiteSamples
{
    /// <summary>

    /// Summary description for ScrollingListBox.

    /// </summary>

    public class ScrollingListBox : System.Windows.Forms.ListBox
    {
        /// <summary> 

        /// Required designer variable.

        /// </summary>

        private System.ComponentModel.Container components = null;

        public ScrollingListBox()
        {
            // This call is required by the Windows.Forms Form Designer.

            InitializeComponent();

            // TODO: Add any initialization after the InitForm call


        }

        /// <summary> 

        /// Clean up any resources being used.

        /// </summary>

        protected override void Dispose( bool disposing )
        {
            if( disposing )
            {
                if(components != null)
                {
                    components.Dispose();
                }
            }
            base.Dispose( disposing );
        }

        #region Component Designer generated code
        /// <summary> 

        /// Required method for Designer support - do not modify 

        /// the contents of this method with the code editor.

        /// </summary>

        private void InitializeComponent()
        {
            components = new System.ComponentModel.Container();
        }
        #endregion
    }
}

Win32 Stuff

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_THUMBPOSITION or 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 );

The 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 'SIF_ALL'.

        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 'Clicked'):

        [Category("Action")]
        public event ScrollEventHandler Scrolled = null;

In the 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(
                            ScrollEventType.EndScroll,
                            si.nPos);
                        Scrolled(this, sargs);
                    }
                }
            }
            base.WndProc(ref msg);
        }

To be able to use the Marshal class, you have to reference the Interop namespace:

using System.Runtime.InteropServices;

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 GetScrollInfo function.

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 ListBox.

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
Generalwill it work properly in Windows Vista ?
Member 4527096
22:48 19 Aug '09  
I have used this scrollable (only vertical)ListBox for my project developed in windows XP and it's working perfectly. but, when i run my application in windows Vista, the scrolling is not working. The scrolling arrow-mark in the listbox is getting vanished after first scroll in windows Vista theme.
Can anybody help me, with any idea to solve this issue ??
GeneralScroll position in wParam of SB_THUMBPOSITION and SB_THUMBTRACK
ZachJohnson
16:00 1 May '09  
For reference, it turns out that the scroll position is sent with SB_THUMBPOSITION and SB_THUMBTRACK, it just has to be extracted from the wParam.

The MSDN documentation says:

"nPos
Value of the high-order word of wParam. Specifies the current position of the scroll box if the nScrollCode parameter is SB_THUMBPOSITION or SB_THUMBTRACK; otherwise, nPos is not used."

The scroll position is the "high-order word of wParam". Also, the scroll code is sent in the low-order word of the wParam, so it might be best to extract the low word from the wParam and determine the scroll type that way, instead of looking at the entire wParam. Here are C# equivalents of the HIWORD and LOWORD C macros which you can use to get the scroll position and scroll code out of the wParam.

public static short LOWORD(int dWord)
{
return (short)dWord;
}

public static short HIWORD(int dWord)
{
return (short)(dWord >> 16);
}

GeneralI made some changes to support vscroll and mouse wheel
Harry Chou
8:11 26 Sep '07  
private const int SB_HORZ = 1;
            private const int WM_MOUSEWHEEL = 0x20A;

....

          protected override void WndProc(ref Message msg)
          {
                  if (msg.Msg == WM_VSCROLL || msg.Msg == WM_MOUSEWHEEL)
                  {
                        if (Scrolled != null)
                        {
                              ScrollInfoStruct si = new ScrollInfoStruct();
                              si.fMask = SIF_ALL;
                              si.cbSize = Marshal.SizeOf(si);
                              GetScrollInfo(msg.HWnd, SB_HORZ, ref si);

                              if (msg.WParam.ToInt32() == SB_ENDSCROLL || msg.Msg == WM_MOUSEWHEEL)
                              {
                                    ScrollEventArgs sargs = new ScrollEventArgs(
                                          ScrollEventType.EndScroll,
                                          si.nPos);
                                    Scrolled(this, sargs);
                              }
                        }
                  }
                  base.WndProc(ref msg);
          }
QuestionnPos = 0, all the time.
chbfiv
18:10 21 Aug '07  
I'm getting the
msg.WParam.ToInt32() == SB_ENDSCROLL

but the si.nPos is always zero...


protected override void WndProc(ref System.Windows.Forms.Message msg)
{

if (msg.Msg == WM_VSCROLL)
{
base.WndProc(ref msg);
if (Scroll != null)
{
SCROLLINFO si = new SCROLLINFO();
si.fMask = SIF_POS;
si.cbSize = Convert.ToUInt32(Marshal.SizeOf(si));
si.nMax = 5000;
GetScrollInfo(msg.HWnd, 0, ref si);

if (msg.WParam.ToInt32() == SB_ENDSCROLL)
{

ScrollEventArgs sargs = new ScrollEventArgs(ScrollEventType.EndScroll, si.nPos );
Scroll(this, sargs);
}
}
}
else base.WndProc(ref msg);
}

AnswerRe: nPos = 0, all the time.
Bob A A
11:55 26 Oct '08  
Change the 0 (Hort Scroll) to 1 (Vert Scroll)

Before:
GetScrollInfo(msg.HWnd, 0, ref si);

After:
GetScrollInfo(msg.HWnd, 1, ref si);
GeneralScrollingListBox VB Code
YogJ69
12:17 23 Aug '06  
Here is VB Code for the ScrollingListBox Class:

Imports system
Imports System.Collections
Imports System.ComponentModel
Imports System.Drawing
Imports System.Data
Imports System.Windows.Forms
Imports System.Runtime.InteropServices


Public Class ScrollingListBox
Inherits System.Windows.Forms.ListBox

Private components As System.ComponentModel.Container = Nothing

_
Public Event Scrolled As ScrollEventHandler

Private Const WM_HSCROLL As Integer = &H114
Private Const WM_VSCROLL As Integer = &H115

Private Const SB_LINELEFT As Integer = 0
Private Const SB_LINERIGHT As Integer = 1
Private Const SB_PAGELEFT As Integer = 2
Private Const SB_PAGERIGHT As Integer = 3
Private Const SB_THUMBPOSITION As Integer = 4
Private Const SB_THUMBTRACK As Integer = 5
Private Const SB_LEFT As Integer = 6
Private Const SB_RIGHT As Integer = 7
Private Const SB_ENDSCROLL As Integer = 8
Private Const SIF_TRACKPOS As Integer = &H10
Private Const SIF_RANGE As Integer = &H1
Private Const SIF_POS As Integer = &H4
Private Const SIF_PAGE As Integer = &H2
Private Const SIF_ALL As Integer = (SIF_RANGE Or SIF_PAGE Or SIF_POS Or SIF_TRACKPOS)

Private Structure ScrollInfoStruct
Public cbSize As Integer
Public fMask As Integer
Public nMin As Integer
Public nMax As Integer
Public nPage As Integer
Public nPos As Integer
Public nTrackPos As Integer
End Structure

_
Private Shared Function GetScrollInfo(ByRef hWnd As Integer, ByVal n As Integer, ByRef lpScrollInfo As ScrollInfoStruct) As Integer
End Function

Protected Overrides Sub WndProc(ByRef msg As System.Windows.Forms.Message)
Dim si As New ScrollInfoStruct

If msg.Msg = WM_VSCROLL Then
'If Scrolled Is System.DBNull.Value = False Then
si.fMask = SIF_ALL
si.cbSize = Marshal.SizeOf(si)
GetScrollInfo(msg.HWnd, 0, si)
If (msg.WParam.ToInt32() = SB_ENDSCROLL) Then
Dim sargs As New ScrollEventArgs(ScrollEventType.EndScroll, _
si.nPos)
RaiseEvent Scrolled(Me, sargs)
End If
'End If
End If
MyBase.WndProc(msg)
End Sub

Private Sub InitializeComponent()
components = New System.ComponentModel.Container()
'Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
End Sub

Protected Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing AndAlso components IsNot Nothing Then
components.Dispose()
End If
MyBase.Dispose(disposing)
End Sub

End Class
GeneralRe: ScrollingListBox VB Code
Bob A A
11:58 26 Oct '08  
Appears this is an error

Private Shared Function GetScrollInfo(ByRef hWnd As Integer, ByVal n As Integer, ByRef lpScrollInfo As ScrollInfoStruct) As Integer
End Function

Should be:

Private Declare Function GetScrollInfo Lib "user32" (ByVal hWnd As IntPtr, ByVal n As Integer, ByRef lpScrollInfo As ScrollInfoStruct) As Integer
GeneralAdding the control to the form
thomasholme
2:19 14 Jan '06  
Hi.

How do you add a custom made control to the Form1 in design view?
I cannot find it on the Toolbox pane and I could not find it when trying to add it to the Toolbar pan.
I cannot figure it out. (sorry%#&!)

/Thomas
Confused
Generalhorizontal scallbar in listbox
Anonymous
21:32 21 Jun '05  
Hi
I want to know how to add horizontal scallbar in listbox with asp.net and vb script

thank you.
GeneralCan i get scroll events for an external application?
llisha
21:11 20 Apr '05  
hi
How can i get the sroll events of an apllication invoked by our program.eg.
i got code for trackking some events of MSWord,but i need both horizontal and vertical events to be track.
lkr

GeneralShould this work for all controls?
mcollins2003
10:10 14 Mar '05  
First of all, this is very helpful code.

Second, I'd like to apply this code to the MS Web browser control to have it fire events when scrolling occurs but can't seem to get it to work. It appears that the WM_HSCROLL messages never occur.

Am I missing something or can we not do this with the web browser control?

Thanks.
QuestionRe: Should this work for all controls?
yuanshu_yun
3:36 11 Jul '06  
I meet the problem too, how can it works with the web browser control?
GeneralHow do i set position of scroll?
dankoGreiner
3:50 25 Feb '05  
When i read a position of scroll i save it, and later want to set scrollposition on the same position.
So, how do i set position of scroll?
GeneralRe: How do i set position of scroll?
clivenyong
8:55 5 Feb '06  
Ya.. Anyone know how to set the scroll position ?

GeneralRe: How do i set position of scroll?
PRMan!!!
14:19 22 Aug '06  
listbox1.TopIndex = ...
Generalsome code for the world
Anonymous
5:42 31 Jan '05  
My goal was to expose a scroll event out of controls (treeview / datagrid) and keep them in sync vertically.

I derived from the ScrollEventArgs normally returned by scroll events (from scrollbars), and added a Horizontal/Vertical enum, plus a percent of max (the idea of a percent max is flawed, because the size of the thumb's page gets in the way.   So percent never reaches 1).   Then when I saw a WM_HSCROLL or WM_VSCROLL out of the control, I would raise my own event.

The listener of this event up in the form then told the sibling control to set its scrollbar to the same place.   The problem I had was that the scrollbar would move, but the base control wouldn't reflect that.   (I was calling SetScrollInfo directly, and noticed that the parent control's WndProc was never called.   My guess is I need to send some sort of "SyncYourself" message.)

While searching for an answer, I ran across the article "TreeListView" here in CodeProject.   This control is much better suited for what I need to do, so I'm abandoning this multi-control syncing.

However, the utility I put together to turn a raw WM_HSCROLL/WM_VSCROLL event into a .net friendly scroll event could still be usefull to people.   I'm going to respond to this, and try to paste the .cs file (we'll see if there is a size limit on these messages)
GeneralRe: some code for the world (ScrollUtility.cs)
Anonymous
5:44 31 Jan '05  
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;

// TODO: Change this namespace to whatever namespace your project is
namespace ScrollExposed
{

#region Public Enums

public enum ScrollEventOrientation
{
Horizontal = 0,
Vertical
};

#endregion

#region Interface: IScrollExposed

/// /// When you derive a control, and want to expose scroll events (using this utility as a helper), have
/// the control implement this interface to give things a consistent look.
///
public interface IScrollExposed
{

// I wanted to call this event Scroll, which is what the horizontal and vertical scroll bars expose. However,
// DataGrid already exposes that event, but it only passes an EventArg, not even a ScrollEventArg, which
// basically makes it useless. (on a side note for the data grid, you can get at its scrollbars directly through
// the Controls property)
//
// If you really want to go nuts, you could make two events: Before and After. It just depends whether you
// raise the event before or after the call to base.WndProc(ref msg);
event ScrollEventHandlerExpanded ScrollExpanded;

// These allow the user of controls to work with the scrollbars without worrying about min/max/value. (or
// even how the scrollbars are implemented internally) I have a felling that a major use of all this scroll
// exposure is to keep 2 or more controls lined up (treeview lined up vertically with a grid).
//
// Here's the case I'm trying to make work:
// There is a treeview on the left, and a grid on the right. The rows of the grid line up with the nodes of the
// treeview (when they expand a node, rows are inserted; collapse causes rows to be deleted). So when the
// user vertically scrolls the treeview or grid, the other needs to stay in sync. Since my scroll event exposes a
// percent, all I need to do is call the set of the other control.
double ScrollBarPercentOfMaxHorizontal
{
get;
set;
}

double ScrollBarPercentOfMaxVertical
{
get;
set;
}


}


#endregion

#region Delegate: ScrollEventHandlerExpanded

public delegate void ScrollEventHandlerExpanded(object sender, ScrollEventArgsExpanded e);

#endregion

#region Class: ScrollEventArgsExpanded

public class ScrollEventArgsExpanded : ScrollEventArgs
{

private ScrollEventOrientation _orientation;
private double _percentOfMax;

public ScrollEventArgsExpanded(ScrollEventOrientation orientation, ScrollEventType type, int newValue, double percentOfMax) : base(type, newValue)
{
_orientation = orientation;
_percentOfMax = percentOfMax;
}

/// /// Tells you what direction the user was scrolling to fire this event
///
public ScrollEventOrientation Orientation
{
get
{
return _orientation;
}
}


/// /// This goes from 0 to 1, and tells how far along the scroll bar is:
/// (Value - Min) / (Max - Min)
///
/// /// This will most likely never be 1. The width of the trackbar gets in the way. However, for keeping 2
/// controls in sync, that shouldn't matter.
///
public double PercentOfMax
{
get
{
return _percentOfMax;
}
}


}


#endregion

#region Class: ScrollUtility

/// /// Several helper functions to change a WndProc message into scroll events
///
/// /// Within your derived control, override WndProc. If the m.Msg equals WM_HSCROLL or WM_VSCROLL, then
/// you can use the helper functions within this class to turn the rest of msg into scroll event args
///
public class ScrollUtility
{

#region Declaration Section

//-------------------------- These constants came from C:\Program Files\Microsoft Visual Studio .NET\Vc7\PlatformSDK\Include\WinUser.h
//
// These are constants that tell you what the m.Msg in WndProc is. ex:
// if(m.Msg == ScrollUtility.WM_HSCROLL)
public const int WM_HSCROLL = 0x114;
public const int WM_VSCROLL = 0x115;

// These are constants that help me decipher the rest of the message from WndProc
private const int SB_LINEUP = 0;
private const int SB_LINELEFT = 0;
private const int SB_LINEDOWN = 1;
private const int SB_LINERIGHT = 1;
private const int SB_PAGEUP = 2;
private const int SB_PAGELEFT = 2;
private const int SB_PAGEDOWN = 3;
private const int SB_PAGERIGHT = 3;
private const int SB_THUMBPOSITION = 4;
private const int SB_THUMBTRACK = 5;
private const int SB_TOP = 6;
private const int SB_LEFT = 6;
private const int SB_BOTTOM = 7;
private const int SB_RIGHT = 7;
private const int SB_ENDSCROLL = 8;

// These are constants that help me call GetScrollInfo (which gives me information about scrollbar position)
private const int SB_HORZ = 0;
private const int SB_VERT = 1;
private const int SB_CTL = 2;
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;

[DllImport("user32.dll", SetLastError=true)]
private static extern int GetScrollInfo( IntPtr hWnd, int n, ref ScrollInfoStruct lpScrollInfo );

[DllImport("user32.dll", SetLastError=true)]
private static extern int SetScrollInfo( IntPtr hWnd, int n, ref ScrollInfoStruct lpScrollInfo, bool fRedraw );


private struct ScrollInfoStruct
{
public int cbSize;
public int fMask;
public int nMin;
public int nMax;
public int nPage;
public int nPos;
public int nTrackPos;
}


#endregion

#region GetNewScrollEventArgsExpanded

public static ScrollEventArgsExpanded GetNewScrollEventArgsExpanded(Message m)
{

// These are the values that make up the return class
ScrollEventOrientation orientation;
ScrollEventType type;
int newValue;
double percentOfMax;

int getscrollinfoOrientationParam;

// Figure out the orientation
switch (m.Msg)
{
case WM_HSCROLL:
orientation = ScrollEventOrientation.Horizontal;
getscrollinfoOrientationParam = SB_HORZ; // This assumes the control has a default horizonal scrollbar. If that's not the case, you might have to get more elaborite
break;

case WM_VSCROLL:
orientation = ScrollEventOrientation.Vertical;
getscrollinfoOrientationParam = SB_VERT; // This assumes the control has a default vertical scrollbar. If that's not the case, you might have to get more elaborite
break;

default:
throw new ArgumentOutOfRangeException("m.Msg", m.Msg, "Only WM_HSCROLL and WM_VSCROLL are supported");
}

// Now I can call GetScrollInfo
ScrollInfoStruct scrollInfo = GetScrollInfoStruct(m.HWnd, getscrollinfoOrientationParam);

// Figure out the type
switch (LOWORD(m.WParam.ToInt32())) // I need to pull the loword of the integer, because ThumbPos and ThumbTrack use the hiword of the integer for something else
{
case SB_THUMBPOSITION:
type = ScrollEventType.ThumbPosition;
break;

case SB_THUMBTRACK:
type = ScrollEventType.ThumbTrack;
break;

case SB_ENDSCROLL:
type = ScrollEventType.EndScroll;
break;

case SB_TOP: // SB_LEFT is the same value
type = ScrollEventType.First;
break;

case SB_BOTTOM: // SB_RIGHT is the same value
type = ScrollEventType.Last;
break;

case SB_PAGEUP: // SB_PAGELEFT is the same value
type = ScrollEventType.LargeDecrement;
break;

case SB_PAGEDOWN: // SB_PAGERIGHT is the same value
type = ScrollEventType.LargeIncrement;
break;

case SB_LINEUP: // SB_LINELEFT is the same value
type = ScrollEventType.SmallDecrement;
break;

case SB_LINEDOWN: // SB_LINERIGHT is the same value
type = ScrollEventType.SmallIncrement;
break;

default:
throw new ArgumentOutOfRangeException("LOWORD(m.WParam.ToInt32())", LOWORD(m.WParam.ToInt32()), "One of SB_ENDSCROLL, SB_THUMBTRACK, etc (between 0 and 8)");
}

// Figure out the value and percent of max
percentOfMax = GetPercentOfMax(scrollInfo.nPos, scrollInfo.nMin, scrollInfo.nMax);
newValue = scrollInfo.nPos;

// Exit Function
return new ScrollEventArgsExpanded(orientation, type, newValue, percentOfMax);
}


#endregion

#region Get/Set ScrollBarPercentOfMax

public static double GetScrollBarPercentOfMaxHorizontal(IntPtr handle)
{

ScrollInfoStruct scrollInfo = GetScrollInfoStruct(handle, SB_HORZ);

return GetPercentOfMax(scrollInfo.nPos, scrollInfo.nMin, scrollInfo.nMax);

}

public static double GetScrollBarPercentOfMaxVertical(IntPtr handle)
{

ScrollInfoStruct scrollInfo = GetScrollInfoStruct(handle, SB_VERT);

return GetPercentOfMax(scrollInfo.nPos, scrollInfo.nMin, scrollInfo.nMax);

}


public static void SetScrollBarPercentOfMaxHorizontal(IntPtr handle, double percent)
{

SetScrollBarPercentOfMax(handle, percent, SB_HORZ);

}

public static void SetScrollBarPercentOfMaxVertical(IntPtr handle, double percent)
{

SetScrollBarPercentOfMax(handle, percent, SB_VERT);

}

private static void SetScrollBarPercentOfMax(IntPtr handle, double percent, int orientation)
{

// Grab a scroll info structure
ScrollInfoStruct scrollInfo = GetScrollInfoStruct(handle, orientation);

// Set the position to something new
scrollInfo.nPos = Convert.ToInt32(Math.Round(Convert.ToDouble(scrollInfo.nMax - scrollInfo.nMin) * percent));

// Make sure it's not out of range (the user needs to pass in a percent from 0 to 1). Even then, there
// may be some mathmatical error
if(scrollInfo.nPos < scrollInfo.nMin)
{
scrollInfo.nPos = scrollInfo.nMin;
}
else if(scrollInfo.nPos > scrollInfo.nMax)
{
scrollInfo.nPos = scrollInfo.nMax;
}

// I don't want to set everything, just position
scrollInfo.fMask = SIF_POS;

// Tell the control to go to this new position
SetScrollInfo(handle, orientation, ref scrollInfo, true); // It may not need to be byref

}


#endregion

#region Private Support Functions

private static int LOWORD(int value32)
{
return value32 & 0xFFFF;
}

private static int LOWORD(IntPtr valuePtr)
{
return LOWORD(valuePtr.ToInt32());
}


private static int HIWORD(int value32)
{

throw new ApplicationException("Downloaded from VB example. Ported to C#, but needs testing");

//Public Function HIWORD(ByRef pintValue As Int32) As Int32
// If (pintValue And &H80000000) = &H80000000 Then
// Return ((pintValue And &H7FFF0000) \ &H10000) Or &H8000&
// Else
// Return (pintValue And &HFFFF0000) \ &H10000
// End If
//End Function

if ((value32 & 0x80000000) == 0x80000000)
{
return ((value32 & 0x7FFF0000) / 0x10000) | 0x8000;
}
else
{
return Convert.ToInt32((value32 & 0xFFFF0000) / 0x10000);
}

}


private static double GetPercentOfMax(int position, int min, int max)
{

double retVal = Convert.ToDouble(position - min) / Convert.ToDouble(max - min);

// Since this function is a helper to the custom controls, this can be called at design time. So I
// need this if statement here. Note: using (retVal == Double.NaN) doesn't work.
if(Double.IsNaN(retVal))
{
return 0;
}
else
{
return retVal;
}

}


/// /// Fills up a ScrollInfoStruct
///
/// The handle to the control /// SB_HORZ or SB_VERT or maybe SB_CTL /// Tells you where the scroll position is (plus min and max of the scrollbar) private static ScrollInfoStruct GetScrollInfoStruct(IntPtr handle, int orientation)
{

// Make the shell of the structure (set two of the elements that form the question)
ScrollInfoStruct retVal = new ScrollInfoStruct();
retVal.fMask = SIF_ALL;
retVal.cbSize = Marshal.SizeOf(retVal);

// Ask for the rest of the structure to be filled out (based on what I just set up)
GetScrollInfo(handle, orientation, ref retVal);

// Exit Function
return retVal;
}


#endregion

}


#endregion

}

GeneralRe: some code for the world (Derived Listbox)
Anonymous
5:49 31 Jan '05  
using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;

namespace ScrollExposed
{

     public class ListboxScrollEvents : System.Windows.Forms.ListBox, IScrollExposed
     {

          //     The scroll event that I expose to the world (raised for both horizontal and vertical scrolling)
          public event ScrollEventHandlerExpanded ScrollExpanded = null;

          #region Auto Form Stuff

          /// <summary>
          /// Required designer variable.
          /// </summary>
          private System.ComponentModel.Container components = null;

          public ListboxScrollEvents()
          {
               // This call is required by the Windows.Forms Form Designer.
               InitializeComponent();

               // TODO: Add any initialization after the InitializeComponent call

          }

          /// <summary>
          /// Clean up any resources being used.
          /// </summary>
          protected override void Dispose( bool disposing )
          {
               if( disposing )
               {
                    if(components != null)
                    {
                         components.Dispose();
                    }
               }
               base.Dispose( disposing );
          }

          #region Component Designer generated code
          /// <summary>
          /// Required method for Designer support - do not modify
          /// the contents of this method with the code editor.
          /// </summary>
          private void InitializeComponent()
          {
               components = new System.ComponentModel.Container();
          }
          #endregion

          #endregion

          public double ScrollBarPercentOfMaxHorizontal
          {
               get
               {
                    return ScrollUtility.GetScrollBarPercentOfMaxHorizontal(base.Handle);
               }
               set
               {
                    ScrollUtility.SetScrollBarPercentOfMaxHorizontal(base.Handle, value);
               }
          }

          public double ScrollBarPercentOfMaxVertical
          {
               get
               {
                    return ScrollUtility.GetScrollBarPercentOfMaxVertical(base.Handle);
               }
               set
               {
                    ScrollUtility.SetScrollBarPercentOfMaxVertical(base.Handle, value);
               }
          }


          protected override void WndProc(ref Message m)
          {
               //     I will do this before I raise any events to get the final scroll position
               base.WndProc(ref m);

               //     See if this is a scroll event
               if(m.Msg == ScrollUtility.WM_HSCROLL || m.Msg == ScrollUtility.WM_VSCROLL)
               {
                    //     I need to raise an event.   See if there is anybody listening
                    if(ScrollExpanded != null)
                    {
                         //     I need to raise a dotnet friendly scroll event.   Build up my args.
                         ScrollEventArgsExpanded arg = ScrollUtility.GetNewScrollEventArgsExpanded(m);

                         //     I have my argument.   Tell the world.
                         ScrollExpanded(this, arg);
                    }
               }

          }


     }
}

GeneralRe: some code for the world (Derived Listbox)
dhawal kapdia
20:17 21 Feb '06  
Hi,

Can this code be used for commercial purpose. If Yes, what is the license agreement for it

Dhawal
GeneralRe: some code for the world (about ScrollUtility.cs)
Anonymous
6:05 31 Jan '05  
I was reading through what I posted, and realized that I forgot to check the "Do not treat <'s as HTML tags", so all the xml comments above the functions are lost.

When you see something like:
///
/// Several helper functions to change a WndProc message into scroll events
///
///
/// Within your derived control, override WndProc. If the m.Msg equals WM_HSCROLL or WM_VSCROLL, then
/// you can use the helper functions within this class to turn the rest of msg into scroll event args
///


It should have been:
     /// <summary>
     /// Several helper functions to change a WndProc message into scroll events
     /// </summary>
     /// <remarks>
     /// Within your derived control, override WndProc.   If the m.Msg equals WM_HSCROLL or WM_VSCROLL, then
     /// you can use the helper functions within this class to turn the rest of msg into scroll event args
     /// </remarks>

GeneralRe: some code for the world (about ScrollUtility.cs)
brianlang75
10:15 3 Jun '05  
I used your code. I'm trying to sync scrolling between two listboxes. so when the first one scrolls, the second scrolls also.

Code:
private void listBox1_ScrollExpanded(object sender, ScrollExposed.ScrollEventArgsExpanded e)
{

if (e.Orientation == ScrollEventOrientation.Horizontal)
{
listBox2.ScrollBarPercentOfMaxHorizontal = e.PercentOfMax;
}
else {
listBox2.ScrollBarPercentOfMaxVertical = e.PercentOfMax;
}
}

When I do this, the scroll bar on the 2nd listbox does move, however the contents of the listbox don't change. Any ideas what I'm doing wrong? Thanks
GeneralRe: some code for the world (about ScrollUtility.cs)
kasix
14:47 16 Nov '05  
try this:

ListBox2.TopIndex = ListBox1.TopIndex;
More info about "ListBox.TopIndex" property find in your VS help.
GeneralRe: some code for the world (about ScrollUtility.cs)
PRMan!!!
14:19 22 Aug '06  
Can you do this both ways? I assume yes.
QuestionRe: some code for the world (about ScrollUtility.cs)
divinaet
22:33 20 Aug '07  
I try to synchronize two textbox but I got the same problem : the scrollbars move but not the content.
The TextBox doesn't have "TopIndex" property... is it possible to scroll the content ?
Generaloriginally Position vs. target Position
thopra
6:47 19 Jan '05  
first of all I want to say, that this is a very useful code. I use this sample to reposition another control, every time the listbox content has been moved horizontally.
After calling
GetScrollInfo(msg.HWnd, 0, ref si);
si.nPos seems to contain the originally position not the target position.

e.g: When I hit the right arrow key the first time I get Position 0. When hit the right arrow key the second time I get Position 6.

Is there a way to get the target position (=Position 6 after the first hit)?


Last Updated 28 Jun 2004 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010