From 431cea92c8b1fdb0460b9e895937983bb95c8ee3 Mon Sep 17 00:00:00 2001 From: Robin Date: Fri, 18 Sep 2015 12:14:47 +0100 Subject: [PATCH] Added DarkScrollBase and DarkScrollView Abstract classes that can be built on top of for scrollable controls. Use DarkScrollBase for non-GDI rendering and DarkScrollView for GDI rendering. --- DarkUI/Controls/DarkScrollBase.cs | 294 ++++++++++++++++++++++++++++++ DarkUI/Controls/DarkScrollView.cs | 60 ++++++ DarkUI/DarkUI.csproj | 3 + 3 files changed, 357 insertions(+) create mode 100644 DarkUI/Controls/DarkScrollBase.cs create mode 100644 DarkUI/Controls/DarkScrollView.cs diff --git a/DarkUI/Controls/DarkScrollBase.cs b/DarkUI/Controls/DarkScrollBase.cs new file mode 100644 index 0000000..9456b20 --- /dev/null +++ b/DarkUI/Controls/DarkScrollBase.cs @@ -0,0 +1,294 @@ +using System; +using System.Drawing; +using System.Windows.Forms; + +namespace DarkUI +{ + public abstract class DarkScrollBase : Control + { + #region Event Region + + public event EventHandler ViewportChanged; + public event EventHandler ContentSizeChanged; + + #endregion + + #region Field Region + + protected readonly DarkScrollBar _vScrollBar; + protected readonly DarkScrollBar _hScrollBar; + + private Size _visibleSize; + private Size _contentSize; + + private Rectangle _viewport; + + private Point _offsetMousePosition; + + #endregion + + #region Property Region + + public Rectangle Viewport + { + get { return _viewport; } + private set + { + _viewport = value; + + if (ViewportChanged != null) + ViewportChanged(this, null); + } + } + + public Size ContentSize + { + get { return _contentSize; } + set + { + _contentSize = value; + UpdateScrollBars(); + + if (ContentSizeChanged != null) + ContentSizeChanged(this, null); + } + } + + public Point OffsetMousePosition + { + get { return _offsetMousePosition; } + } + + #endregion + + #region Constructor Region + + protected DarkScrollBase() + { + SetStyle(ControlStyles.Selectable | + ControlStyles.UserMouse, true); + + _vScrollBar = new DarkScrollBar { ScrollOrientation = DarkOrientation.Vertical }; + _hScrollBar = new DarkScrollBar { ScrollOrientation = DarkOrientation.Horizontal }; + + Controls.Add(_vScrollBar); + Controls.Add(_hScrollBar); + + _vScrollBar.ValueChanged += delegate { UpdateViewport(); }; + _hScrollBar.ValueChanged += delegate { UpdateViewport(); }; + + _vScrollBar.MouseDown += delegate { Select(); }; + _hScrollBar.MouseDown += delegate { Select(); }; + } + + #endregion + + #region Event Handler Region + + protected override void OnCreateControl() + { + base.OnCreateControl(); + + UpdateScrollBars(); + } + + protected override void OnGotFocus(EventArgs e) + { + base.OnGotFocus(e); + + Invalidate(); + } + + protected override void OnLostFocus(EventArgs e) + { + base.OnLostFocus(e); + + Invalidate(); + } + + protected override void OnResize(EventArgs e) + { + base.OnResize(e); + + UpdateScrollBars(); + } + + protected override void OnMouseMove(MouseEventArgs e) + { + base.OnMouseMove(e); + + _offsetMousePosition = new Point(e.X + Viewport.Left, e.Y + Viewport.Top); + } + + protected override void OnMouseDown(MouseEventArgs e) + { + base.OnMouseDown(e); + + if (e.Button == MouseButtons.Right) + Select(); + } + + protected override void OnMouseWheel(MouseEventArgs e) + { + base.OnMouseWheel(e); + + var horizontal = false; + + if (_hScrollBar.Visible && ModifierKeys == Keys.Control) + horizontal = true; + + if (_hScrollBar.Visible && !_vScrollBar.Visible) + horizontal = true; + + if (!horizontal) + { + if (e.Delta > 0) + _vScrollBar.ScrollByPhysical(3); + else if (e.Delta < 0) + _vScrollBar.ScrollByPhysical(-3); + } + else + { + if (e.Delta > 0) + _hScrollBar.ScrollByPhysical(3); + else if (e.Delta < 0) + _hScrollBar.ScrollByPhysical(-3); + } + } + + protected override void OnPreviewKeyDown(PreviewKeyDownEventArgs e) + { + base.OnPreviewKeyDown(e); + + // Allows arrow keys to trigger OnKeyPress + switch (e.KeyCode) + { + case Keys.Up: + case Keys.Down: + case Keys.Left: + case Keys.Right: + e.IsInputKey = true; + break; + } + } + + #endregion + + #region Method Region + + private void UpdateScrollBars() + { + if (_vScrollBar.Maximum != ContentSize.Height) + _vScrollBar.Maximum = ContentSize.Height; + + if (_hScrollBar.Maximum != ContentSize.Width) + _hScrollBar.Maximum = ContentSize.Width; + + var scrollSize = Consts.ScrollBarSize; + + _vScrollBar.Location = new Point(ClientSize.Width - scrollSize, 0); + _vScrollBar.Size = new Size(scrollSize, ClientSize.Height); + + _hScrollBar.Location = new Point(0, ClientSize.Height - scrollSize); + _hScrollBar.Size = new Size(ClientSize.Width, scrollSize); + + if (DesignMode) + return; + + // Do this twice in case changing the visibility of the scrollbars + // causes the VisibleSize to change in such a way as to require a second scrollbar. + // Probably a better way to detect that scenario... + SetVisibleSize(); + SetScrollBarVisibility(); + SetVisibleSize(); + SetScrollBarVisibility(); + + if (_vScrollBar.Visible) + _hScrollBar.Width -= scrollSize; + + if (_hScrollBar.Visible) + _vScrollBar.Height -= scrollSize; + + _vScrollBar.ViewSize = _visibleSize.Height; + _hScrollBar.ViewSize = _visibleSize.Width; + + UpdateViewport(); + } + + private void SetScrollBarVisibility() + { + _vScrollBar.Visible = _visibleSize.Height < ContentSize.Height; + _hScrollBar.Visible = _visibleSize.Width < ContentSize.Width; + } + + private void SetVisibleSize() + { + var scrollSize = Consts.ScrollBarSize; + + _visibleSize = new Size(ClientSize.Width, ClientSize.Height); + + if (_vScrollBar.Visible) + _visibleSize.Width -= scrollSize; + + if (_hScrollBar.Visible) + _visibleSize.Height -= scrollSize; + } + + private void UpdateViewport() + { + var left = 0; + var top = 0; + var width = ClientSize.Width; + var height = ClientSize.Height; + + if (_hScrollBar.Visible) + { + left = _hScrollBar.Value; + height -= _hScrollBar.Height; + } + + if (_vScrollBar.Visible) + { + top = _vScrollBar.Value; + width -= _vScrollBar.Width; + } + + Viewport = new Rectangle(left, top, width, height); + + var pos = PointToClient(MousePosition); + _offsetMousePosition = new Point(pos.X + Viewport.Left, pos.Y + Viewport.Top); + + Invalidate(); + } + + public void ScrollTo(Point point) + { + HScrollTo(point.X); + VScrollTo(point.Y); + } + + public void VScrollTo(int value) + { + if (_vScrollBar.Visible) + _vScrollBar.Value = value; + } + + public void HScrollTo(int value) + { + if (_hScrollBar.Visible) + _hScrollBar.Value = value; + } + + public Point PointToView(Point point) + { + return new Point(point.X - Viewport.Left, point.Y - Viewport.Top); + } + + public Rectangle RectangleToView(Rectangle rect) + { + return new Rectangle(new Point(rect.Left - Viewport.Left, rect.Top - Viewport.Top), rect.Size); + } + + #endregion + } +} diff --git a/DarkUI/Controls/DarkScrollView.cs b/DarkUI/Controls/DarkScrollView.cs new file mode 100644 index 0000000..c6f9270 --- /dev/null +++ b/DarkUI/Controls/DarkScrollView.cs @@ -0,0 +1,60 @@ +using System.Drawing; +using System.Windows.Forms; + +namespace DarkUI +{ + public abstract class DarkScrollView : DarkScrollBase + { + #region Constructor Region + + protected DarkScrollView() + { + SetStyle(ControlStyles.OptimizedDoubleBuffer | + ControlStyles.ResizeRedraw | + ControlStyles.UserPaint, true); + } + + #endregion + + #region Paint Region + + protected abstract void PaintContent(Graphics g); + + protected override void OnPaint(PaintEventArgs e) + { + var g = e.Graphics; + + // Draw background + using (var b = new SolidBrush(BackColor)) + { + g.FillRectangle(b, ClientRectangle); + } + + // Offset the graphics based on the viewport, render the control contents, then reset it. + g.TranslateTransform(Viewport.Left * -1, Viewport.Top * -1); + + PaintContent(g); + + g.TranslateTransform(Viewport.Left, Viewport.Top); + + // Draw the bit where the scrollbars meet + if (_vScrollBar.Visible && _hScrollBar.Visible) + { + using (var b = new SolidBrush(BackColor)) + { + var rect = new Rectangle(_hScrollBar.Right, _vScrollBar.Bottom, _vScrollBar.Width, + _hScrollBar.Height); + + g.FillRectangle(b, rect); + } + } + } + + protected override void OnPaintBackground(PaintEventArgs e) + { + // Absorb event + } + + #endregion + } +} diff --git a/DarkUI/DarkUI.csproj b/DarkUI/DarkUI.csproj index 2516111..4b113af 100644 --- a/DarkUI/DarkUI.csproj +++ b/DarkUI/DarkUI.csproj @@ -41,6 +41,9 @@ + + Component + Component