AceInfinity
Emeritus, Contributor
Here's something I just put together. I think it would be useful in a few projects. I created this based on something that I'm currently working on, but it is by no means a complete solution. It works though.
The main issue i'm having is dynamically measuring the height of the slider for a vertical scrollbar, width for a horizontal, at runtime, as this changes depending on how many lines, or how long the longest line is, in the textbox.
Therefore in my code below, right now MaxHorizontal and MaxVertical are really useless, until I can take the nPos, and add the height/width of the vertical/horizontal slider, to compare.
I can easily measure for when the scrollbar is at position 0 though (min).
Nonetheless, here is an extended textbox class that now has official scrollbar events.
Source Code:
Example Usage:
The main issue i'm having is dynamically measuring the height of the slider for a vertical scrollbar, width for a horizontal, at runtime, as this changes depending on how many lines, or how long the longest line is, in the textbox.
Therefore in my code below, right now MaxHorizontal and MaxVertical are really useless, until I can take the nPos, and add the height/width of the vertical/horizontal slider, to compare.
I can easily measure for when the scrollbar is at position 0 though (min).
Nonetheless, here is an extended textbox class that now has official scrollbar events.
Source Code:
Code:
// Name: Scroller TextBox
// Author: AceInfinity
// Copyright: Tech.Reboot.Pro 2013
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;
class ScrollTextBox : TextBox
{
// Win32 Functions
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetScrollInfo(IntPtr hwnd, int fnBar, ref SCROLLINFO lpsi);
// Nested Types
[Serializable, StructLayout(LayoutKind.Sequential)]
struct SCROLLINFO
{
public uint cbSize;
public uint fMask;
public int nMin;
public int nMax;
public uint nPage;
public int nPos;
public int nTrackPos;
}
public enum ScrollInfoMask : uint
{
SIF_RANGE = 0x1,
SIF_PAGE = 0x2,
SIF_POS = 0x4,
SIF_DISABLENOSCROLL = 0x8,
SIF_TRACKPOS = 0x10,
SIF_ALL = (SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS)
}
// Constants
private const int SB_HORZ = 0;
private const int SB_VERT = 1;
private const int WM_HSCROLL = 0x0114;
private const int WM_VSCROLL = 0x0115;
private const int WM_MOUSEWHEEL = 0x020A;
private const int WM_KEYDOWN = 0x0100;
private const int VK_UP = 0x26;
private const int VK_DOWN = 0x28;
private const int VK_LEFT = 0x25;
private const int VK_RIGHT = 0x27;
// Properties
private int _VerticalScrollPos = 0;
public int VerticalScrollPos
{
get { return _VerticalScrollPos; }
}
private int _HorizontalScrollPos = 0;
public int HorizontalScrollPos
{
get { return _HorizontalScrollPos; }
}
private int MinHorizontal, MinVertical, MaxHorizontal, MaxVertical;
// Constructor
public ScrollTextBox()
{
this.DoubleBuffered = true;
this.Multiline = true;
this.WordWrap = false;
this.ScrollBars = ScrollBars.Both;
}
// Overrides
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
bool raiseEvent = false;
switch (m.Msg)
{
case WM_HSCROLL:
{
ScrollCode nScrollCode = (ScrollCode)loWord(m.WParam.ToInt32());
SCROLLINFO si = new SCROLLINFO();
si.cbSize = (uint)Marshal.SizeOf(si);
si.fMask = (int)ScrollInfoMask.SIF_ALL;
if (GetScrollInfo(this.Handle, SB_HORZ, ref si))
{
raiseEvent = si.nPos != _HorizontalScrollPos;
_HorizontalScrollPos = si.nPos;
MinHorizontal = si.nMin;
MaxHorizontal = si.nMax;
}
if (raiseEvent)
{
ScrollPosChanged(this, new ScrollStateChangedEventArgs(_VerticalScrollPos, _HorizontalScrollPos, nScrollCode));
}
}
break;
case WM_VSCROLL:
{
ScrollCode nScrollCode = (ScrollCode)loWord(m.WParam.ToInt32());
SCROLLINFO si = new SCROLLINFO();
si.cbSize = (uint)Marshal.SizeOf(si);
si.fMask = (int)ScrollInfoMask.SIF_ALL;
if (GetScrollInfo(this.Handle, SB_VERT, ref si))
{
raiseEvent = si.nPos != _VerticalScrollPos;
_VerticalScrollPos = si.nPos;
MinVertical = si.nMin;
MaxVertical = si.nMax;
}
if (raiseEvent)
{
ScrollPosChanged(this, new ScrollStateChangedEventArgs(_VerticalScrollPos, _HorizontalScrollPos, nScrollCode));
}
}
break;
case WM_MOUSEWHEEL:
{
ScrollCode nScrollCode = (ScrollCode)loWord(m.WParam.ToInt32());
SCROLLINFO si = new SCROLLINFO();
si.cbSize = (uint)Marshal.SizeOf(si);
si.fMask = (int)ScrollInfoMask.SIF_ALL;
if (GetScrollInfo(this.Handle, SB_VERT, ref si))
{
raiseEvent = si.nPos != _VerticalScrollPos;
_VerticalScrollPos = si.nPos;
MinVertical = si.nMin;
MaxVertical = si.nMax;
}
if (raiseEvent)
{
ScrollPosChanged(this, new ScrollStateChangedEventArgs(_VerticalScrollPos, _HorizontalScrollPos, nScrollCode));
}
}
break;
case WM_KEYDOWN:
{
ScrollCode nScrollCode = (ScrollCode)loWord(m.WParam.ToInt32());
SCROLLINFO si = new SCROLLINFO();
si.cbSize = (uint)Marshal.SizeOf(si);
si.fMask = (int)ScrollInfoMask.SIF_ALL;
if (m.WParam.ToInt32() == VK_UP || m.WParam.ToInt32() == VK_DOWN)
{
if (GetScrollInfo(this.Handle, SB_VERT, ref si))
{
raiseEvent = si.nPos != _VerticalScrollPos;
_VerticalScrollPos = si.nPos;
MinVertical = si.nMin;
MaxVertical = si.nMax;
}
}
else if (m.WParam.ToInt32() == VK_LEFT || m.WParam.ToInt32() == VK_RIGHT)
{
if (GetScrollInfo(this.Handle, SB_HORZ, ref si))
{
raiseEvent = si.nPos != _HorizontalScrollPos;
_HorizontalScrollPos = si.nPos;
MinVertical = si.nMin;
MaxVertical = si.nMax;
}
}
if (raiseEvent)
{
ScrollPosChanged(this, new ScrollStateChangedEventArgs(_VerticalScrollPos, _HorizontalScrollPos, nScrollCode));
}
}
break;
}
}
// Methods
private static int loWord(int dwInt)
{
return 0xFFFF & dwInt;
}
// Events
public delegate void ScrollStateChanged(object sender, ScrollStateChangedEventArgs e);
public event ScrollStateChanged ScrollPosChanged;
protected virtual void OnScrollPosChanged(ScrollStateChangedEventArgs e)
{
ScrollStateChanged handler = ScrollPosChanged;
if (handler != null)
{
handler(this, e);
}
}
}
public enum ScrollCode
{
SB_LINEUP = 0, // Scrolled one line up
SB_LINEDOWN = 1, // Scrolled one line down
SB_PAGEUP = 2, // Scrolled one page up
SB_PAGEDOWN = 3, // Scrolled one page down
SB_THUMBPOSITION = 4, // Dragged and released scrollbar with mouse
SB_THUMBTRACK = 5, // Dragging the scrollbar
SB_TOP = 6, // Scroll to upper left
SB_BOTTOM = 7, // Scroll to bottom right
SB_ENDSCROLL = 8 // Ends scroll
}
public class ScrollStateChangedEventArgs : EventArgs
{
public ScrollStateChangedEventArgs(int vertical, int horizontal, ScrollCode nScrollCode)
{
_VerticalPosition = vertical;
_HorizontalPosition = horizontal;
_ScrollBarCode = nScrollCode;
}
private ScrollCode _ScrollBarCode;
public ScrollCode ScrollBarCode
{
get { return _ScrollBarCode; }
}
private int _VerticalPosition = 0;
public int VerticalPosition
{
get { return _VerticalPosition; }
}
private int _HorizontalPosition = 0;
public int HorizontalPosition
{
get { return _HorizontalPosition; }
}
}
Example Usage:
Code:
bool TestWithEndScroll = false;
private void scrollTextBox1_VerticalScrollPosChanged(object sender, Controls.ScrollStateChangedEventArgs e)
{
if (TestWithEndScroll)
{
if (e.ScrollBarCode == ScrollCode.SB_ENDSCROLL)
{
label1.Text = string.Format("Vertical Scroll Pos: {0}", e.VerticalPosition);
label2.Text = string.Format("Horizontal Scroll Pos: {0}", e.HorizontalPosition);
}
}
else
{
label1.Text = string.Format("Vertical Scroll Pos: {0}", e.VerticalPosition);
label2.Text = string.Format("Horizontal Scroll Pos: {0}", e.HorizontalPosition);
}
}
Last edited: