[C#] Extended TextBox Class - Scroll Events

AceInfinity

Emeritus, Contributor
Joined
Feb 21, 2012
Posts
1,728
Location
Canada
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.

GWhS6YH.gif


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:
Great stuff!

For info - We have custom coding in place here at Sysnative that allows you to change the length of the code-box.

Place =123 (any number) after CODE.
 

Has Sysnative Forums helped you? Please consider donating to help us support the site!

Back
Top