123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215 |
- using System;
- using System.ComponentModel;
- using System.Windows.Forms;
- namespace MarkdownRenderer.Test
- {
- /// <summary>
- /// TextBox with support for getting and setting the vertical scroll bar
- /// position, as well as listening to vertical scroll events.
- /// </summary>
- public class ScrollTextBox : RichTextBox
- {
- public ScrollTextBox()
- {
- _components = new Container();
- // Calculate width of "W" to set as the small horizontal increment
- OnFontChanged(null);
- }
- [DefaultValue(0)]
- [Category("Appearance")]
- [Description("Gets or sets the vertical scroll bar's position")]
- public int VerticalScrollPosition
- {
- set => SetScroll(value, Win32.WM_VSCROLL, Win32.SB_VERT);
- get => GetScroll(Win32.SB_VERT);
- }
- [DefaultValue(0)]
- [Category("Appearance")]
- [Description("Gets or sets the horizontal scroll bar's position")]
- public int HorizontalScrollPosition
- {
- set => SetScroll(value, Win32.WM_HSCROLL, Win32.SB_HORZ);
- get => GetScroll(Win32.SB_HORZ);
- }
- [Description("Fired when the scroll bar's vertical position changes")]
- [Category("Property Changed")]
- public event ScrollEventHandler ScrollChanged;
- // Fire scroll event if the scroll-bars are moved
- protected override void WndProc(ref Message message)
- {
- base.WndProc(ref message);
- if (message.Msg == Win32.WM_VSCROLL
- || message.Msg == Win32.WM_HSCROLL
- || message.Msg == Win32.WM_MOUSEWHEEL
- ) TryFireScrollEvent();
- }
- // Key-down includes navigation keys, but also typing and pasting can
- // cause a scroll event
- protected override void OnKeyDown(KeyEventArgs e)
- {
- base.OnKeyDown(e);
- TryFireScrollEvent();
- }
- protected override void OnKeyUp(KeyEventArgs e)
- {
- base.OnKeyUp(e);
- TryFireScrollEvent();
- }
- // Resizing can alter the word-wrap or fit the content causing the text
- // to scroll
- protected override void OnResize(EventArgs e)
- {
- base.OnResize(e);
- TryFireScrollEvent();
- }
- // Clicking an empty space can move the carot to the beginning of the
- // line causing a scroll event
- protected override void OnMouseDown(MouseEventArgs e)
- {
- base.OnMouseDown(e);
- TryFireScrollEvent();
- }
- protected override void OnMouseUp(MouseEventArgs e)
- {
- base.OnMouseUp(e);
- TryFireScrollEvent();
- }
- // If the mouse button is down, a text selection is probably being done
- // where dragging the selection can caues scroll events
- protected override void OnMouseMove(MouseEventArgs e)
- {
- base.OnMouseMove(e);
- if (e.Button != MouseButtons.None) TryFireScrollEvent();
- }
- // Changing the size of the font can cause a scroll event. Also, when
- // scrolling horizontally, the event will notify whether the scroll
- // was a large or small change. For vertical, small increments are 1
- // line, but for horizontal, it is several pixels. To guess what a
- // small increment is, get the width of the W character and anything
- // smaller than that will be represented as a small increment
- protected override void OnFontChanged(EventArgs e)
- {
- base.OnFontChanged(e);
- using (var graphics = CreateGraphics()) _fontWidth = (int)graphics.MeasureString("W", Font).Width;
- TryFireScrollEvent();
- }
- protected override void Dispose(bool disposing)
- {
- if (disposing) _components?.Dispose();
- base.Dispose(disposing);
- }
- private void SetScroll(int value, uint windowsMessage, int scrollBarMessage)
- {
- Win32.SetScrollPos(Handle, scrollBarMessage, value, true);
- Win32.PostMessage(Handle, windowsMessage, 4 + 0x10000 * value, 0);
- }
- private int GetScroll(int scrollBarMessage) => Win32.GetScrollPos(Handle, scrollBarMessage);
- // Fire both horizontal and vertical scroll events seperately, one
- // after the other. These first test if a scroll actually occurred
- // and won't fire if there was no actual movement
- private void TryFireScrollEvent()
- {
- // Don't do anything if there is no event handler
- if (ScrollChanged == null) return;
- TryFireHorizontalScrollEvent();
- TryFireVerticalScrollEvent();
- }
- private void TryFireHorizontalScrollEvent()
- {
- // Don't do anything if there is no event handler
- if (ScrollChanged == null) return;
- var lastScrollPosition = _lastHorizontalScrollPosition;
- var scrollPosition = HorizontalScrollPosition;
- // Don't do anything if there was no change in position
- if (scrollPosition == lastScrollPosition) return;
- _lastHorizontalScrollPosition = scrollPosition;
- ScrollChanged(this
- , new ScrollEventArgs(
- scrollPosition < lastScrollPosition - _fontWidth
- ? ScrollEventType.LargeDecrement
- : scrollPosition > lastScrollPosition + _fontWidth
- ? ScrollEventType.LargeIncrement
- : scrollPosition < lastScrollPosition
- ? ScrollEventType.SmallDecrement
- : ScrollEventType.SmallIncrement
- , lastScrollPosition
- , scrollPosition
- , ScrollOrientation.HorizontalScroll
- )
- );
- }
- private void TryFireVerticalScrollEvent()
- {
- // Don't do anything if there is no event handler
- if (ScrollChanged == null) return;
- var lastScrollPosition = _lastVerticalScrollPosition;
- var scrollPosition = VerticalScrollPosition;
- // Don't do anything if there was no change in position
- if (scrollPosition == lastScrollPosition) return;
- _lastVerticalScrollPosition = scrollPosition;
- ScrollChanged(this
- , new ScrollEventArgs(
- scrollPosition < lastScrollPosition - 1
- ? ScrollEventType.LargeDecrement
- : scrollPosition > lastScrollPosition + 1
- ? ScrollEventType.LargeIncrement
- : scrollPosition < lastScrollPosition
- ? ScrollEventType.SmallDecrement
- : ScrollEventType.SmallIncrement
- , lastScrollPosition
- , scrollPosition
- , ScrollOrientation.VerticalScroll
- )
- );
- }
- private int _lastVerticalScrollPosition;
- private int _lastHorizontalScrollPosition;
- private int _fontWidth;
- private IContainer _components = null;
- private static class Win32
- {
- public const uint WM_HSCROLL = 0x114;
- public const uint WM_VSCROLL = 0x115;
- public const uint WM_MOUSEWHEEL = 0x20A;
- public const int SB_VERT = 0x1;
- public const int SB_HORZ = 0x0;
- [System.Runtime.InteropServices.DllImport("user32.dll")]
- public static extern int SetScrollPos(IntPtr hWnd, int nBar, int nPos, bool bRedraw);
- [System.Runtime.InteropServices.DllImport("user32.dll")]
- public static extern int GetScrollPos(IntPtr hWnd, int nBar);
- [System.Runtime.InteropServices.DllImport("User32.Dll", EntryPoint = "PostMessageA")]
- public static extern bool PostMessage(IntPtr hWnd, uint msg, int wParam, int lParam);
- }
- }
- }
|