 |
|
 |
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); }
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
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); }
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
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); }
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Change the 0 (Hort Scroll) to 1 (Vert Scroll)
Before: GetScrollInfo(msg.HWnd, 0, ref si);
After: GetScrollInfo(msg.HWnd, 1, ref si);
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
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
|
| Sign In·View Thread·PermaLink | 2.00/5 (1 vote) |
|
|
|
 |
|
 |
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
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
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
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
 |
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
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
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.
|
| Sign In·View Thread·PermaLink | 2.00/5 (1 vote) |
|
|
|
 |
|
|
 |
|
 |
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?
|
| Sign In·View Thread·PermaLink | 2.80/5 (4 votes) |
|
|
|
 |
|
|
 |
|
|
 |
|
 |
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)
|
| Sign In·View Thread·PermaLink | 5.00/5 (1 vote) |
|
|
|
 |
|
 |
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
}
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
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); } }
}
} }
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
 |
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>
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
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
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
try this:
ListBox2.TopIndex = ListBox1.TopIndex;
More info about "ListBox.TopIndex" property find in your VS help.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
 |
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 ?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
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)?
|
| Sign In·View Thread·PermaLink | 2.00/5 (1 vote) |
|
|
|
 |
|
|
 |