kopia lustrzana https://github.com/dl2alf/AirScout
1897 wiersze
57 KiB
C#
1897 wiersze
57 KiB
C#
namespace CustomScrollBar
|
|
{
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Drawing;
|
|
using System.Runtime.InteropServices;
|
|
using System.Windows.Forms;
|
|
|
|
/// <summary>
|
|
/// A custom scrollbar control.
|
|
/// </summary>
|
|
[Designer(typeof(Design.ScrollBarControlDesigner))]
|
|
[DefaultEvent("Scroll")]
|
|
[DefaultProperty("Value")]
|
|
public class ScrollBarEx : Control
|
|
{
|
|
#region fields
|
|
|
|
/// <summary>
|
|
/// Redraw const.
|
|
/// </summary>
|
|
private const int SETREDRAW = 11;
|
|
|
|
/// <summary>
|
|
/// Indicates many changes to the scrollbar are happening, so stop painting till finished.
|
|
/// </summary>
|
|
private bool inUpdate;
|
|
|
|
/// <summary>
|
|
/// The scrollbar orientation - horizontal / vertical.
|
|
/// </summary>
|
|
private ScrollBarOrientation orientation = ScrollBarOrientation.Vertical;
|
|
|
|
/// <summary>
|
|
/// The scroll orientation in scroll events.
|
|
/// </summary>
|
|
private ScrollOrientation scrollOrientation = ScrollOrientation.VerticalScroll;
|
|
|
|
/// <summary>
|
|
/// The clicked channel rectangle.
|
|
/// </summary>
|
|
private Rectangle clickedBarRectangle;
|
|
|
|
/// <summary>
|
|
/// The thumb rectangle.
|
|
/// </summary>
|
|
private Rectangle thumbRectangle;
|
|
|
|
/// <summary>
|
|
/// The top arrow rectangle.
|
|
/// </summary>
|
|
private Rectangle topArrowRectangle;
|
|
|
|
/// <summary>
|
|
/// The bottom arrow rectangle.
|
|
/// </summary>
|
|
private Rectangle bottomArrowRectangle;
|
|
|
|
/// <summary>
|
|
/// The channel rectangle.
|
|
/// </summary>
|
|
private Rectangle channelRectangle;
|
|
|
|
/// <summary>
|
|
/// Indicates if top arrow was clicked.
|
|
/// </summary>
|
|
private bool topArrowClicked;
|
|
|
|
/// <summary>
|
|
/// Indicates if bottom arrow was clicked.
|
|
/// </summary>
|
|
private bool bottomArrowClicked;
|
|
|
|
/// <summary>
|
|
/// Indicates if channel rectangle above the thumb was clicked.
|
|
/// </summary>
|
|
private bool topBarClicked;
|
|
|
|
/// <summary>
|
|
/// Indicates if channel rectangle under the thumb was clicked.
|
|
/// </summary>
|
|
private bool bottomBarClicked;
|
|
|
|
/// <summary>
|
|
/// Indicates if the thumb was clicked.
|
|
/// </summary>
|
|
private bool thumbClicked;
|
|
|
|
/// <summary>
|
|
/// The state of the thumb.
|
|
/// </summary>
|
|
private ScrollBarState thumbState = ScrollBarState.Normal;
|
|
|
|
/// <summary>
|
|
/// The state of the top arrow.
|
|
/// </summary>
|
|
private ScrollBarArrowButtonState topButtonState = ScrollBarArrowButtonState.UpNormal;
|
|
|
|
/// <summary>
|
|
/// The state of the bottom arrow.
|
|
/// </summary>
|
|
private ScrollBarArrowButtonState bottomButtonState = ScrollBarArrowButtonState.DownNormal;
|
|
|
|
/// <summary>
|
|
/// The scrollbar value minimum.
|
|
/// </summary>
|
|
private int minimum;
|
|
|
|
/// <summary>
|
|
/// The scrollbar value maximum.
|
|
/// </summary>
|
|
private int maximum = 100;
|
|
|
|
/// <summary>
|
|
/// The small change value.
|
|
/// </summary>
|
|
private int smallChange = 1;
|
|
|
|
/// <summary>
|
|
/// The large change value.
|
|
/// </summary>
|
|
private int largeChange = 10;
|
|
|
|
/// <summary>
|
|
/// The value of the scrollbar.
|
|
/// </summary>
|
|
private int value;
|
|
|
|
/// <summary>
|
|
/// The width of the thumb.
|
|
/// </summary>
|
|
private int thumbWidth = 15;
|
|
|
|
/// <summary>
|
|
/// The height of the thumb.
|
|
/// </summary>
|
|
private int thumbHeight;
|
|
|
|
/// <summary>
|
|
/// The width of an arrow.
|
|
/// </summary>
|
|
private int arrowWidth = 15;
|
|
|
|
/// <summary>
|
|
/// The height of an arrow.
|
|
/// </summary>
|
|
private int arrowHeight = 17;
|
|
|
|
/// <summary>
|
|
/// The bottom limit for the thumb bottom.
|
|
/// </summary>
|
|
private int thumbBottomLimitBottom;
|
|
|
|
/// <summary>
|
|
/// The bottom limit for the thumb top.
|
|
/// </summary>
|
|
private int thumbBottomLimitTop;
|
|
|
|
/// <summary>
|
|
/// The top limit for the thumb top.
|
|
/// </summary>
|
|
private int thumbTopLimit;
|
|
|
|
/// <summary>
|
|
/// The current position of the thumb.
|
|
/// </summary>
|
|
private int thumbPosition;
|
|
|
|
/// <summary>
|
|
/// The track position.
|
|
/// </summary>
|
|
private int trackPosition;
|
|
|
|
/// <summary>
|
|
/// The progress timer for moving the thumb.
|
|
/// </summary>
|
|
private Timer progressTimer = new Timer();
|
|
|
|
/// <summary>
|
|
/// The border color.
|
|
/// </summary>
|
|
private Color borderColor = Color.FromArgb(93, 140, 201);
|
|
|
|
/// <summary>
|
|
/// The border color in disabled state.
|
|
/// </summary>
|
|
private Color disabledBorderColor = Color.Gray;
|
|
|
|
/// <summary>
|
|
/// The list of background markers.
|
|
/// </summary>
|
|
private List<int> backgroundmarkers;
|
|
|
|
/// <summary>
|
|
/// The color of background markers.
|
|
/// </summary>
|
|
private Color backgroundmarkercolor = Color.DarkGray;
|
|
|
|
#region context menu items
|
|
|
|
/// <summary>
|
|
/// Context menu strip.
|
|
/// </summary>
|
|
private ContextMenuStrip contextMenu;
|
|
|
|
/// <summary>
|
|
/// Container for components.
|
|
/// </summary>
|
|
private IContainer components;
|
|
|
|
/// <summary>
|
|
/// Menu item.
|
|
/// </summary>
|
|
private ToolStripMenuItem tsmiScrollHere;
|
|
|
|
/// <summary>
|
|
/// Menu separator.
|
|
/// </summary>
|
|
private ToolStripSeparator toolStripSeparator1;
|
|
|
|
/// <summary>
|
|
/// Menu item.
|
|
/// </summary>
|
|
private ToolStripMenuItem tsmiTop;
|
|
|
|
/// <summary>
|
|
/// Menu item.
|
|
/// </summary>
|
|
private ToolStripMenuItem tsmiBottom;
|
|
|
|
/// <summary>
|
|
/// Menu separator.
|
|
/// </summary>
|
|
private ToolStripSeparator toolStripSeparator2;
|
|
|
|
/// <summary>
|
|
/// Menu item.
|
|
/// </summary>
|
|
private ToolStripMenuItem tsmiLargeUp;
|
|
|
|
/// <summary>
|
|
/// Menu item.
|
|
/// </summary>
|
|
private ToolStripMenuItem tsmiLargeDown;
|
|
|
|
/// <summary>
|
|
/// Menu separator.
|
|
/// </summary>
|
|
private ToolStripSeparator toolStripSeparator3;
|
|
|
|
/// <summary>
|
|
/// Menu item.
|
|
/// </summary>
|
|
private ToolStripMenuItem tsmiSmallUp;
|
|
|
|
/// <summary>
|
|
/// Menu item.
|
|
/// </summary>
|
|
private ToolStripMenuItem tsmiSmallDown;
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region constructor
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="ScrollBarEx"/> class.
|
|
/// </summary>
|
|
public ScrollBarEx()
|
|
{
|
|
// sets the control styles of the control
|
|
SetStyle(
|
|
ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw
|
|
| ControlStyles.Selectable | ControlStyles.AllPaintingInWmPaint
|
|
| ControlStyles.UserPaint, true);
|
|
|
|
// initializes the context menu
|
|
this.InitializeComponent();
|
|
|
|
this.Width = 19;
|
|
this.Height = 200;
|
|
|
|
// sets the scrollbar up
|
|
this.SetUpScrollBar();
|
|
|
|
// timer for clicking and holding the mouse button
|
|
// over/below the thumb and on the arrow buttons
|
|
this.progressTimer.Interval = 20;
|
|
this.progressTimer.Tick += this.ProgressTimerTick;
|
|
|
|
// no image margin in context menu
|
|
this.contextMenu.ShowImageMargin = false;
|
|
this.ContextMenuStrip = this.contextMenu;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region events
|
|
/// <summary>
|
|
/// Occurs when the scrollbar scrolled.
|
|
/// </summary>
|
|
[Category("Behavior")]
|
|
[Description("Is raised, when the scrollbar was scrolled.")]
|
|
public event ScrollEventHandler Scroll;
|
|
#endregion
|
|
|
|
#region properties
|
|
|
|
/// <summary>
|
|
/// Gets or sets the orientation.
|
|
/// </summary>
|
|
[Category("Layout")]
|
|
[Description("Gets or sets the orientation.")]
|
|
[DefaultValue(ScrollBarOrientation.Vertical)]
|
|
public ScrollBarOrientation Orientation
|
|
{
|
|
get
|
|
{
|
|
return this.orientation;
|
|
}
|
|
|
|
set
|
|
{
|
|
// no change - return
|
|
if (value == this.orientation)
|
|
{
|
|
return;
|
|
}
|
|
|
|
this.orientation = value;
|
|
|
|
// change text of context menu entries
|
|
this.ChangeContextMenuItems();
|
|
|
|
// save scroll orientation for scroll event
|
|
this.scrollOrientation = value == ScrollBarOrientation.Vertical ?
|
|
ScrollOrientation.VerticalScroll : ScrollOrientation.HorizontalScroll;
|
|
|
|
// only in DesignMode switch width and height
|
|
if (this.DesignMode)
|
|
{
|
|
this.Size = new Size(this.Height, this.Width);
|
|
}
|
|
|
|
// sets the scrollbar up
|
|
this.SetUpScrollBar();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the minimum value.
|
|
/// </summary>
|
|
[Category("Behavior")]
|
|
[Description("Gets or sets the minimum value.")]
|
|
[DefaultValue(0)]
|
|
public int Minimum
|
|
{
|
|
get
|
|
{
|
|
return this.minimum;
|
|
}
|
|
|
|
set
|
|
{
|
|
// no change or value invalid - return
|
|
if (this.minimum == value || value < 0 || value >= this.maximum)
|
|
{
|
|
return;
|
|
}
|
|
|
|
this.minimum = value;
|
|
|
|
// current value less than new minimum value - adjust
|
|
if (this.value < value)
|
|
{
|
|
this.value = value;
|
|
}
|
|
|
|
// is current large change value invalid - adjust
|
|
if (this.largeChange > this.maximum - this.minimum)
|
|
{
|
|
this.largeChange = this.maximum - this.minimum;
|
|
}
|
|
|
|
this.SetUpScrollBar();
|
|
|
|
// current value less than new minimum value - adjust
|
|
if (this.value < value)
|
|
{
|
|
this.Value = value;
|
|
}
|
|
else
|
|
{
|
|
// current value is valid - adjust thumb position
|
|
this.ChangeThumbPosition(this.GetThumbPosition());
|
|
|
|
this.Refresh();
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the maximum value.
|
|
/// </summary>
|
|
[Category("Behavior")]
|
|
[Description("Gets or sets the maximum value.")]
|
|
[DefaultValue(100)]
|
|
public int Maximum
|
|
{
|
|
get
|
|
{
|
|
return this.maximum;
|
|
}
|
|
|
|
set
|
|
{
|
|
// no change or new max. value invalid - return
|
|
if (value == this.maximum || value < 1 || value <= this.minimum)
|
|
{
|
|
return;
|
|
}
|
|
|
|
this.maximum = value;
|
|
|
|
// is large change value invalid - adjust
|
|
if (this.largeChange > this.maximum - this.minimum)
|
|
{
|
|
this.largeChange = this.maximum - this.minimum;
|
|
}
|
|
|
|
this.SetUpScrollBar();
|
|
|
|
// is current value greater than new maximum value - adjust
|
|
if (this.value > value)
|
|
{
|
|
this.Value = this.maximum;
|
|
}
|
|
else
|
|
{
|
|
// current value is valid - adjust thumb position
|
|
this.ChangeThumbPosition(this.GetThumbPosition());
|
|
|
|
this.Refresh();
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the small change amount.
|
|
/// </summary>
|
|
[Category("Behavior")]
|
|
[Description("Gets or sets the small change value.")]
|
|
[DefaultValue(1)]
|
|
public int SmallChange
|
|
{
|
|
get
|
|
{
|
|
return this.smallChange;
|
|
}
|
|
|
|
set
|
|
{
|
|
// no change or new small change value invalid - return
|
|
if (value == this.smallChange || value < 1 || value >= this.largeChange)
|
|
{
|
|
return;
|
|
}
|
|
|
|
this.smallChange = value;
|
|
|
|
this.SetUpScrollBar();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the large change amount.
|
|
/// </summary>
|
|
[Category("Behavior")]
|
|
[Description("Gets or sets the large change value.")]
|
|
[DefaultValue(10)]
|
|
public int LargeChange
|
|
{
|
|
get
|
|
{
|
|
return this.largeChange;
|
|
}
|
|
|
|
set
|
|
{
|
|
// no change or new large change value is invalid - return
|
|
if (value == this.largeChange || value < this.smallChange || value < 2)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// if value is greater than scroll area - adjust
|
|
if (value > this.maximum - this.minimum)
|
|
{
|
|
this.largeChange = this.maximum - this.minimum;
|
|
}
|
|
else
|
|
{
|
|
// set new value
|
|
this.largeChange = value;
|
|
}
|
|
|
|
this.SetUpScrollBar();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the value.
|
|
/// </summary>
|
|
[Category("Behavior")]
|
|
[Description("Gets or sets the current value.")]
|
|
[DefaultValue(0)]
|
|
public int Value
|
|
{
|
|
get
|
|
{
|
|
return this.value;
|
|
}
|
|
|
|
set
|
|
{
|
|
// no change or invalid value - return
|
|
if (this.value == value || value < this.minimum || value > this.maximum)
|
|
{
|
|
return;
|
|
}
|
|
|
|
this.value = value;
|
|
|
|
// adjust thumb position
|
|
this.ChangeThumbPosition(this.GetThumbPosition());
|
|
|
|
// raise scroll event
|
|
this.OnScroll(new ScrollEventArgs(ScrollEventType.ThumbPosition, -1, this.value, this.scrollOrientation));
|
|
|
|
this.Refresh();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the list of background markers
|
|
/// </summary>
|
|
[Category("Behavior")]
|
|
[Description("Gets or sets the list of background markers.")]
|
|
[DefaultValue(null)]
|
|
public List<int> BackgroundMarkers
|
|
{
|
|
get
|
|
{
|
|
return this.backgroundmarkers;
|
|
}
|
|
|
|
set
|
|
{
|
|
this.backgroundmarkers = value;
|
|
this.Refresh();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the border color.
|
|
/// </summary>
|
|
[Category("Appearance")]
|
|
[Description("Gets or sets the border color.")]
|
|
[DefaultValue(typeof(Color), "93, 140, 201")]
|
|
public Color BorderColor
|
|
{
|
|
get
|
|
{
|
|
return this.borderColor;
|
|
}
|
|
|
|
set
|
|
{
|
|
this.borderColor = value;
|
|
|
|
this.Invalidate();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the border color in disabled state.
|
|
/// </summary>
|
|
[Category("Appearance")]
|
|
[Description("Gets or sets the border color in disabled state.")]
|
|
[DefaultValue(typeof(Color), "Gray")]
|
|
public Color DisabledBorderColor
|
|
{
|
|
get
|
|
{
|
|
return this.disabledBorderColor;
|
|
}
|
|
|
|
set
|
|
{
|
|
this.disabledBorderColor = value;
|
|
|
|
this.Invalidate();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the opacity of the context menu (from 0 - 1).
|
|
/// </summary>
|
|
[Category("Appearance")]
|
|
[Description("Gets or sets the opacity of the context menu (from 0 - 1).")]
|
|
[DefaultValue(typeof(double), "1")]
|
|
public double Opacity
|
|
{
|
|
get
|
|
{
|
|
return this.contextMenu.Opacity;
|
|
}
|
|
|
|
set
|
|
{
|
|
// no change - return
|
|
if (value == this.contextMenu.Opacity)
|
|
{
|
|
return;
|
|
}
|
|
|
|
this.contextMenu.AllowTransparency = value != 1;
|
|
|
|
this.contextMenu.Opacity = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the color of background markers
|
|
/// </summary>
|
|
[Category("Behavior")]
|
|
[DefaultValue(typeof(Color), "DarkGray")]
|
|
[Description("Gets or sets the color of background markers.")]
|
|
public Color BackgroundMarkerColor
|
|
{
|
|
get
|
|
{
|
|
return this.backgroundmarkercolor;
|
|
}
|
|
|
|
set
|
|
{
|
|
this.backgroundmarkercolor = value;
|
|
this.Refresh();
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region methods
|
|
|
|
#region public methods
|
|
|
|
/// <summary>
|
|
/// Prevents the drawing of the control until <see cref="EndUpdate"/> is called.
|
|
/// </summary>
|
|
public void BeginUpdate()
|
|
{
|
|
SendMessage(this.Handle, SETREDRAW, false, 0);
|
|
this.inUpdate = true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ends the updating process and the control can draw itself again.
|
|
/// </summary>
|
|
public void EndUpdate()
|
|
{
|
|
SendMessage(this.Handle, SETREDRAW, true, 0);
|
|
this.inUpdate = false;
|
|
this.SetUpScrollBar();
|
|
this.Refresh();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region protected methods
|
|
|
|
/// <summary>
|
|
/// Raises the <see cref="Scroll"/> event.
|
|
/// </summary>
|
|
/// <param name="e">The <see cref="ScrollEventArgs"/> that contains the event data.</param>
|
|
protected virtual void OnScroll(ScrollEventArgs e)
|
|
{
|
|
// if event handler is attached - raise scroll event
|
|
if (this.Scroll != null)
|
|
{
|
|
this.Scroll(this, e);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Paints the background of the control.
|
|
/// </summary>
|
|
/// <param name="e">A <see cref="PaintEventArgs"/> that contains information about the control to paint.</param>
|
|
protected override void OnPaintBackground(PaintEventArgs e)
|
|
{
|
|
// no painting here
|
|
}
|
|
|
|
/// <summary>
|
|
/// Paints the control.
|
|
/// </summary>
|
|
/// <param name="e">A <see cref="PaintEventArgs"/> that contains information about the control to paint.</param>
|
|
protected override void OnPaint(PaintEventArgs e)
|
|
{
|
|
// sets the smoothing mode to none
|
|
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
|
|
|
|
// save client rectangle
|
|
Rectangle rect = ClientRectangle;
|
|
|
|
// adjust the rectangle
|
|
if (this.orientation == ScrollBarOrientation.Vertical)
|
|
{
|
|
rect.X++;
|
|
rect.Y += this.arrowHeight + 1;
|
|
rect.Width -= 2;
|
|
rect.Height -= (this.arrowHeight * 2) + 2;
|
|
}
|
|
else
|
|
{
|
|
rect.X += this.arrowWidth + 1;
|
|
rect.Y++;
|
|
rect.Width -= (this.arrowWidth * 2) + 2;
|
|
rect.Height -= 2;
|
|
}
|
|
|
|
// draws the background
|
|
ScrollBarExRenderer.DrawBackground(
|
|
e.Graphics,
|
|
ClientRectangle,
|
|
this.orientation);
|
|
|
|
// draws background markers, if any
|
|
if ((backgroundmarkers != null) && (backgroundmarkers.Count > 0))
|
|
{
|
|
Pen pen = new Pen(backgroundmarkercolor);
|
|
foreach (int bm in backgroundmarkers)
|
|
{
|
|
int pos = (int)(((double)bm - (double)Minimum) / ((double)Maximum - (double)Minimum) * ClientRectangle.Width);
|
|
e.Graphics.DrawLine(pen, new Point(pos, ClientRectangle.Top), new Point(pos, ClientRectangle.Bottom));
|
|
}
|
|
}
|
|
// draws the track
|
|
ScrollBarExRenderer.DrawTrack(
|
|
e.Graphics,
|
|
rect,
|
|
ScrollBarState.Normal,
|
|
this.orientation);
|
|
|
|
// draw thumb and grip
|
|
ScrollBarExRenderer.DrawThumb(
|
|
e.Graphics,
|
|
this.thumbRectangle,
|
|
this.thumbState,
|
|
this.orientation);
|
|
|
|
if (this.Enabled)
|
|
{
|
|
ScrollBarExRenderer.DrawThumbGrip(
|
|
e.Graphics,
|
|
this.thumbRectangle,
|
|
this.orientation);
|
|
}
|
|
|
|
// draw arrows
|
|
ScrollBarExRenderer.DrawArrowButton(
|
|
e.Graphics,
|
|
this.topArrowRectangle,
|
|
this.topButtonState,
|
|
true,
|
|
this.orientation);
|
|
|
|
ScrollBarExRenderer.DrawArrowButton(
|
|
e.Graphics,
|
|
this.bottomArrowRectangle,
|
|
this.bottomButtonState,
|
|
false,
|
|
this.orientation);
|
|
|
|
// check if top or bottom bar was clicked
|
|
if (this.topBarClicked)
|
|
{
|
|
if (this.orientation == ScrollBarOrientation.Vertical)
|
|
{
|
|
this.clickedBarRectangle.Y = this.thumbTopLimit;
|
|
this.clickedBarRectangle.Height =
|
|
this.thumbRectangle.Y - this.thumbTopLimit;
|
|
}
|
|
else
|
|
{
|
|
this.clickedBarRectangle.X = this.thumbTopLimit;
|
|
this.clickedBarRectangle.Width =
|
|
this.thumbRectangle.X - this.thumbTopLimit;
|
|
}
|
|
|
|
ScrollBarExRenderer.DrawTrack(
|
|
e.Graphics,
|
|
this.clickedBarRectangle,
|
|
ScrollBarState.Pressed,
|
|
this.orientation);
|
|
}
|
|
else if (this.bottomBarClicked)
|
|
{
|
|
if (this.orientation == ScrollBarOrientation.Vertical)
|
|
{
|
|
this.clickedBarRectangle.Y = this.thumbRectangle.Bottom + 1;
|
|
this.clickedBarRectangle.Height =
|
|
this.thumbBottomLimitBottom - this.clickedBarRectangle.Y + 1;
|
|
}
|
|
else
|
|
{
|
|
this.clickedBarRectangle.X = this.thumbRectangle.Right + 1;
|
|
this.clickedBarRectangle.Width =
|
|
this.thumbBottomLimitBottom - this.clickedBarRectangle.X + 1;
|
|
}
|
|
|
|
ScrollBarExRenderer.DrawTrack(
|
|
e.Graphics,
|
|
this.clickedBarRectangle,
|
|
ScrollBarState.Pressed,
|
|
this.orientation);
|
|
}
|
|
|
|
// draw border
|
|
using (Pen pen = new Pen(
|
|
(this.Enabled ? this.borderColor : this.disabledBorderColor)))
|
|
{
|
|
e.Graphics.DrawRectangle(pen, 0, 0, this.Width - 1, this.Height - 1);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Raises the MouseDown event.
|
|
/// </summary>
|
|
/// <param name="e">A <see cref="MouseEventArgs"/> that contains the event data.</param>
|
|
protected override void OnMouseDown(MouseEventArgs e)
|
|
{
|
|
base.OnMouseDown(e);
|
|
|
|
this.Focus();
|
|
|
|
if (e.Button == MouseButtons.Left)
|
|
{
|
|
// prevents showing the context menu if pressing the right mouse
|
|
// button while holding the left
|
|
this.ContextMenuStrip = null;
|
|
|
|
Point mouseLocation = e.Location;
|
|
|
|
if (this.thumbRectangle.Contains(mouseLocation))
|
|
{
|
|
this.thumbClicked = true;
|
|
this.thumbPosition = this.orientation == ScrollBarOrientation.Vertical ? mouseLocation.Y - this.thumbRectangle.Y : mouseLocation.X - this.thumbRectangle.X;
|
|
this.thumbState = ScrollBarState.Pressed;
|
|
|
|
Invalidate(this.thumbRectangle);
|
|
}
|
|
else if (this.topArrowRectangle.Contains(mouseLocation))
|
|
{
|
|
this.topArrowClicked = true;
|
|
this.topButtonState = ScrollBarArrowButtonState.UpPressed;
|
|
|
|
this.Invalidate(this.topArrowRectangle);
|
|
|
|
this.ProgressThumb(true);
|
|
}
|
|
else if (this.bottomArrowRectangle.Contains(mouseLocation))
|
|
{
|
|
this.bottomArrowClicked = true;
|
|
this.bottomButtonState = ScrollBarArrowButtonState.DownPressed;
|
|
|
|
this.Invalidate(this.bottomArrowRectangle);
|
|
|
|
this.ProgressThumb(true);
|
|
}
|
|
else
|
|
{
|
|
this.trackPosition =
|
|
this.orientation == ScrollBarOrientation.Vertical ?
|
|
mouseLocation.Y : mouseLocation.X;
|
|
|
|
if (this.trackPosition <
|
|
(this.orientation == ScrollBarOrientation.Vertical ?
|
|
this.thumbRectangle.Y : this.thumbRectangle.X))
|
|
{
|
|
this.topBarClicked = true;
|
|
}
|
|
else
|
|
{
|
|
this.bottomBarClicked = true;
|
|
}
|
|
|
|
this.ProgressThumb(true);
|
|
}
|
|
}
|
|
else if (e.Button == MouseButtons.Right)
|
|
{
|
|
this.trackPosition =
|
|
this.orientation == ScrollBarOrientation.Vertical ? e.Y : e.X;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Raises the MouseUp event.
|
|
/// </summary>
|
|
/// <param name="e">A <see cref="MouseEventArgs"/> that contains the event data.</param>
|
|
protected override void OnMouseUp(MouseEventArgs e)
|
|
{
|
|
base.OnMouseUp(e);
|
|
|
|
if (e.Button == MouseButtons.Left)
|
|
{
|
|
this.ContextMenuStrip = this.contextMenu;
|
|
|
|
if (this.thumbClicked)
|
|
{
|
|
this.thumbClicked = false;
|
|
this.thumbState = ScrollBarState.Normal;
|
|
|
|
this.OnScroll(new ScrollEventArgs(
|
|
ScrollEventType.EndScroll,
|
|
-1,
|
|
this.value,
|
|
this.scrollOrientation)
|
|
);
|
|
}
|
|
else if (this.topArrowClicked)
|
|
{
|
|
this.topArrowClicked = false;
|
|
this.topButtonState = ScrollBarArrowButtonState.UpNormal;
|
|
this.StopTimer();
|
|
}
|
|
else if (this.bottomArrowClicked)
|
|
{
|
|
this.bottomArrowClicked = false;
|
|
this.bottomButtonState = ScrollBarArrowButtonState.DownNormal;
|
|
this.StopTimer();
|
|
}
|
|
else if (this.topBarClicked)
|
|
{
|
|
this.topBarClicked = false;
|
|
this.StopTimer();
|
|
}
|
|
else if (this.bottomBarClicked)
|
|
{
|
|
this.bottomBarClicked = false;
|
|
this.StopTimer();
|
|
}
|
|
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Raises the MouseEnter event.
|
|
/// </summary>
|
|
/// <param name="e">A <see cref="EventArgs"/> that contains the event data.</param>
|
|
protected override void OnMouseEnter(EventArgs e)
|
|
{
|
|
base.OnMouseEnter(e);
|
|
|
|
this.bottomButtonState = ScrollBarArrowButtonState.DownActive;
|
|
this.topButtonState = ScrollBarArrowButtonState.UpActive;
|
|
this.thumbState = ScrollBarState.Active;
|
|
|
|
Invalidate();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Raises the MouseLeave event.
|
|
/// </summary>
|
|
/// <param name="e">A <see cref="EventArgs"/> that contains the event data.</param>
|
|
protected override void OnMouseLeave(EventArgs e)
|
|
{
|
|
base.OnMouseLeave(e);
|
|
|
|
this.ResetScrollStatus();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Raises the MouseMove event.
|
|
/// </summary>
|
|
/// <param name="e">A <see cref="MouseEventArgs"/> that contains the event data.</param>
|
|
protected override void OnMouseMove(MouseEventArgs e)
|
|
{
|
|
base.OnMouseMove(e);
|
|
|
|
// moving and holding the left mouse button
|
|
if (e.Button == MouseButtons.Left)
|
|
{
|
|
// Update the thumb position, if the new location is within the bounds.
|
|
if (this.thumbClicked)
|
|
{
|
|
int oldScrollValue = this.value;
|
|
|
|
this.topButtonState = ScrollBarArrowButtonState.UpActive;
|
|
this.bottomButtonState = ScrollBarArrowButtonState.DownActive;
|
|
int pos = this.orientation == ScrollBarOrientation.Vertical ?
|
|
e.Location.Y : e.Location.X;
|
|
|
|
// The thumb is all the way to the top
|
|
if (pos <= (this.thumbTopLimit + this.thumbPosition))
|
|
{
|
|
this.ChangeThumbPosition(this.thumbTopLimit);
|
|
|
|
this.value = this.minimum;
|
|
}
|
|
else if (pos >= (this.thumbBottomLimitTop + this.thumbPosition))
|
|
{
|
|
// The thumb is all the way to the bottom
|
|
this.ChangeThumbPosition(this.thumbBottomLimitTop);
|
|
|
|
this.value = this.maximum;
|
|
}
|
|
else
|
|
{
|
|
// The thumb is between the ends of the track.
|
|
this.ChangeThumbPosition(pos - this.thumbPosition);
|
|
|
|
int pixelRange, thumbPos, arrowSize;
|
|
|
|
// calculate the value - first some helper variables
|
|
// dependent on the current orientation
|
|
if (this.orientation == ScrollBarOrientation.Vertical)
|
|
{
|
|
pixelRange = this.Height - (2 * this.arrowHeight) - this.thumbHeight;
|
|
thumbPos = this.thumbRectangle.Y;
|
|
arrowSize = this.arrowHeight;
|
|
}
|
|
else
|
|
{
|
|
pixelRange = this.Width - (2 * this.arrowWidth) - this.thumbWidth;
|
|
thumbPos = this.thumbRectangle.X;
|
|
arrowSize = this.arrowWidth;
|
|
}
|
|
|
|
float perc = 0f;
|
|
|
|
if (pixelRange != 0)
|
|
{
|
|
// percent of the new position
|
|
perc = (float)(thumbPos - arrowSize) / (float)pixelRange;
|
|
}
|
|
|
|
// the new value is somewhere between max and min, starting
|
|
// at min position
|
|
this.value = Convert.ToInt32((perc * (this.maximum - this.minimum)) + this.minimum);
|
|
}
|
|
|
|
// raise scroll event if new value different
|
|
if (oldScrollValue != this.value)
|
|
{
|
|
this.OnScroll(new ScrollEventArgs(ScrollEventType.ThumbTrack, oldScrollValue, this.value, this.scrollOrientation));
|
|
|
|
this.Refresh();
|
|
}
|
|
}
|
|
}
|
|
else if (!this.ClientRectangle.Contains(e.Location))
|
|
{
|
|
this.ResetScrollStatus();
|
|
}
|
|
else if (e.Button == MouseButtons.None) // only moving the mouse
|
|
{
|
|
if (this.topArrowRectangle.Contains(e.Location))
|
|
{
|
|
this.topButtonState = ScrollBarArrowButtonState.UpHot;
|
|
|
|
this.Invalidate(this.topArrowRectangle);
|
|
}
|
|
else if (this.bottomArrowRectangle.Contains(e.Location))
|
|
{
|
|
this.bottomButtonState = ScrollBarArrowButtonState.DownHot;
|
|
|
|
Invalidate(this.bottomArrowRectangle);
|
|
}
|
|
else if (this.thumbRectangle.Contains(e.Location))
|
|
{
|
|
this.thumbState = ScrollBarState.Hot;
|
|
|
|
this.Invalidate(this.thumbRectangle);
|
|
}
|
|
else if (this.ClientRectangle.Contains(e.Location))
|
|
{
|
|
this.topButtonState = ScrollBarArrowButtonState.UpActive;
|
|
this.bottomButtonState = ScrollBarArrowButtonState.DownActive;
|
|
this.thumbState = ScrollBarState.Active;
|
|
|
|
Invalidate();
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Performs the work of setting the specified bounds of this control.
|
|
/// </summary>
|
|
/// <param name="x">The new x value of the control.</param>
|
|
/// <param name="y">The new y value of the control.</param>
|
|
/// <param name="width">The new width value of the control.</param>
|
|
/// <param name="height">The new height value of the control.</param>
|
|
/// <param name="specified">A bitwise combination of the <see cref="BoundsSpecified"/> values.</param>
|
|
protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified)
|
|
{
|
|
// only in design mode - constrain size
|
|
if (this.DesignMode)
|
|
{
|
|
if (this.orientation == ScrollBarOrientation.Vertical)
|
|
{
|
|
if (height < (2 * this.arrowHeight) + 10)
|
|
{
|
|
height = (2 * this.arrowHeight) + 10;
|
|
}
|
|
|
|
width = 19;
|
|
}
|
|
else
|
|
{
|
|
if (width < (2 * this.arrowWidth) + 10)
|
|
{
|
|
width = (2 * this.arrowWidth) + 10;
|
|
}
|
|
|
|
height = 19;
|
|
}
|
|
}
|
|
|
|
base.SetBoundsCore(x, y, width, height, specified);
|
|
|
|
if (this.DesignMode)
|
|
{
|
|
this.SetUpScrollBar();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Raises the <see cref="System.Windows.Forms.Control.SizeChanged"/> event.
|
|
/// </summary>
|
|
/// <param name="e">An <see cref="EventArgs"/> that contains the event data.</param>
|
|
protected override void OnSizeChanged(EventArgs e)
|
|
{
|
|
base.OnSizeChanged(e);
|
|
this.SetUpScrollBar();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Processes a dialog key.
|
|
/// </summary>
|
|
/// <param name="keyData">One of the <see cref="System.Windows.Forms.Keys"/> values that represents the key to process.</param>
|
|
/// <returns>true, if the key was processed by the control, false otherwise.</returns>
|
|
protected override bool ProcessDialogKey(Keys keyData)
|
|
{
|
|
// key handling is here - keys recognized by the control
|
|
// Up&Down or Left&Right, PageUp, PageDown, Home, End
|
|
Keys keyUp = Keys.Up;
|
|
Keys keyDown = Keys.Down;
|
|
|
|
if (this.orientation == ScrollBarOrientation.Horizontal)
|
|
{
|
|
keyUp = Keys.Left;
|
|
keyDown = Keys.Right;
|
|
}
|
|
|
|
if (keyData == keyUp)
|
|
{
|
|
this.Value -= this.smallChange;
|
|
|
|
return true;
|
|
}
|
|
|
|
if (keyData == keyDown)
|
|
{
|
|
this.Value += this.smallChange;
|
|
|
|
return true;
|
|
}
|
|
|
|
if (keyData == Keys.PageUp)
|
|
{
|
|
this.Value = this.GetValue(false, true);
|
|
|
|
return true;
|
|
}
|
|
|
|
if (keyData == Keys.PageDown)
|
|
{
|
|
if (this.value + this.largeChange > this.maximum)
|
|
{
|
|
this.Value = this.maximum;
|
|
}
|
|
else
|
|
{
|
|
this.Value += this.largeChange;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
if (keyData == Keys.Home)
|
|
{
|
|
this.Value = this.minimum;
|
|
|
|
return true;
|
|
}
|
|
|
|
if (keyData == Keys.End)
|
|
{
|
|
this.Value = this.maximum;
|
|
|
|
return true;
|
|
}
|
|
|
|
return base.ProcessDialogKey(keyData);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Raises the <see cref="System.Windows.Forms.Control.EnabledChanged"/> event.
|
|
/// </summary>
|
|
/// <param name="e">An <see cref="EventArgs"/> that contains the event data.</param>
|
|
protected override void OnEnabledChanged(EventArgs e)
|
|
{
|
|
base.OnEnabledChanged(e);
|
|
|
|
if (this.Enabled)
|
|
{
|
|
this.thumbState = ScrollBarState.Normal;
|
|
this.topButtonState = ScrollBarArrowButtonState.UpNormal;
|
|
this.bottomButtonState = ScrollBarArrowButtonState.DownNormal;
|
|
}
|
|
else
|
|
{
|
|
this.thumbState = ScrollBarState.Disabled;
|
|
this.topButtonState = ScrollBarArrowButtonState.UpDisabled;
|
|
this.bottomButtonState = ScrollBarArrowButtonState.DownDisabled;
|
|
}
|
|
|
|
this.Refresh();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region misc methods
|
|
|
|
/// <summary>
|
|
/// Sends a message.
|
|
/// </summary>
|
|
/// <param name="wnd">The handle of the control.</param>
|
|
/// <param name="msg">The message as int.</param>
|
|
/// <param name="param">param - true or false.</param>
|
|
/// <param name="lparam">Additional parameter.</param>
|
|
/// <returns>0 or error code.</returns>
|
|
/// <remarks>Needed for sending the stop/start drawing of the control.</remarks>
|
|
[DllImport("user32.dll")]
|
|
private static extern int SendMessage(
|
|
IntPtr wnd,
|
|
int msg,
|
|
bool param,
|
|
int lparam);
|
|
|
|
/// <summary>
|
|
/// Sets up the scrollbar.
|
|
/// </summary>
|
|
private void SetUpScrollBar()
|
|
{
|
|
// if no drawing - return
|
|
if (this.inUpdate)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// set up the width's, height's and rectangles for the different
|
|
// elements
|
|
if (this.orientation == ScrollBarOrientation.Vertical)
|
|
{
|
|
this.arrowHeight = 17;
|
|
this.arrowWidth = 15;
|
|
this.thumbWidth = 15;
|
|
this.thumbHeight = this.GetThumbSize();
|
|
|
|
this.clickedBarRectangle = this.ClientRectangle;
|
|
this.clickedBarRectangle.Inflate(-1, -1);
|
|
this.clickedBarRectangle.Y += this.arrowHeight;
|
|
this.clickedBarRectangle.Height -= this.arrowHeight * 2;
|
|
|
|
this.channelRectangle = this.clickedBarRectangle;
|
|
|
|
this.thumbRectangle = new Rectangle(
|
|
ClientRectangle.X + 2,
|
|
ClientRectangle.Y + this.arrowHeight + 1,
|
|
this.thumbWidth - 1,
|
|
this.thumbHeight
|
|
);
|
|
|
|
this.topArrowRectangle = new Rectangle(
|
|
ClientRectangle.X + 2,
|
|
ClientRectangle.Y + 1,
|
|
this.arrowWidth,
|
|
this.arrowHeight
|
|
);
|
|
|
|
this.bottomArrowRectangle = new Rectangle(
|
|
ClientRectangle.X + 2,
|
|
ClientRectangle.Bottom - this.arrowHeight - 1,
|
|
this.arrowWidth,
|
|
this.arrowHeight
|
|
);
|
|
|
|
// Set the default starting thumb position.
|
|
this.thumbPosition = this.thumbRectangle.Height / 2;
|
|
|
|
// Set the bottom limit of the thumb's bottom border.
|
|
this.thumbBottomLimitBottom =
|
|
ClientRectangle.Bottom - this.arrowHeight - 2;
|
|
|
|
// Set the bottom limit of the thumb's top border.
|
|
this.thumbBottomLimitTop =
|
|
this.thumbBottomLimitBottom - this.thumbRectangle.Height;
|
|
|
|
// Set the top limit of the thumb's top border.
|
|
this.thumbTopLimit = ClientRectangle.Y + this.arrowHeight + 1;
|
|
}
|
|
else
|
|
{
|
|
this.arrowHeight = 15;
|
|
this.arrowWidth = 17;
|
|
this.thumbHeight = 15;
|
|
this.thumbWidth = this.GetThumbSize();
|
|
|
|
this.clickedBarRectangle = this.ClientRectangle;
|
|
this.clickedBarRectangle.Inflate(-1, -1);
|
|
this.clickedBarRectangle.X += this.arrowWidth;
|
|
this.clickedBarRectangle.Width -= this.arrowWidth * 2;
|
|
|
|
this.channelRectangle = this.clickedBarRectangle;
|
|
|
|
this.thumbRectangle = new Rectangle(
|
|
ClientRectangle.X + this.arrowWidth + 1,
|
|
ClientRectangle.Y + 2,
|
|
this.thumbWidth,
|
|
this.thumbHeight - 1
|
|
);
|
|
|
|
this.topArrowRectangle = new Rectangle(
|
|
ClientRectangle.X + 1,
|
|
ClientRectangle.Y + 2,
|
|
this.arrowWidth,
|
|
this.arrowHeight
|
|
);
|
|
|
|
this.bottomArrowRectangle = new Rectangle(
|
|
ClientRectangle.Right - this.arrowWidth - 1,
|
|
ClientRectangle.Y + 2,
|
|
this.arrowWidth,
|
|
this.arrowHeight
|
|
);
|
|
|
|
// Set the default starting thumb position.
|
|
this.thumbPosition = this.thumbRectangle.Width / 2;
|
|
|
|
// Set the bottom limit of the thumb's bottom border.
|
|
this.thumbBottomLimitBottom =
|
|
ClientRectangle.Right - this.arrowWidth - 2;
|
|
|
|
// Set the bottom limit of the thumb's top border.
|
|
this.thumbBottomLimitTop =
|
|
this.thumbBottomLimitBottom - this.thumbRectangle.Width;
|
|
|
|
// Set the top limit of the thumb's top border.
|
|
this.thumbTopLimit = ClientRectangle.X + this.arrowWidth + 1;
|
|
}
|
|
|
|
this.ChangeThumbPosition(this.GetThumbPosition());
|
|
|
|
this.Refresh();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handles the updating of the thumb.
|
|
/// </summary>
|
|
/// <param name="sender">The sender.</param>
|
|
/// <param name="e">An object that contains the event data.</param>
|
|
private void ProgressTimerTick(object sender, EventArgs e)
|
|
{
|
|
this.ProgressThumb(true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Resets the scroll status of the scrollbar.
|
|
/// </summary>
|
|
private void ResetScrollStatus()
|
|
{
|
|
// get current mouse position
|
|
Point pos = this.PointToClient(Cursor.Position);
|
|
|
|
// set appearance of buttons in relation to where the mouse is -
|
|
// outside or inside the control
|
|
if (this.ClientRectangle.Contains(pos))
|
|
{
|
|
this.bottomButtonState = ScrollBarArrowButtonState.DownActive;
|
|
this.topButtonState = ScrollBarArrowButtonState.UpActive;
|
|
}
|
|
else
|
|
{
|
|
this.bottomButtonState = ScrollBarArrowButtonState.DownNormal;
|
|
this.topButtonState = ScrollBarArrowButtonState.UpNormal;
|
|
}
|
|
|
|
// set appearance of thumb
|
|
this.thumbState = this.thumbRectangle.Contains(pos) ?
|
|
ScrollBarState.Hot : ScrollBarState.Normal;
|
|
|
|
this.bottomArrowClicked = this.bottomBarClicked =
|
|
this.topArrowClicked = this.topBarClicked = false;
|
|
|
|
this.StopTimer();
|
|
|
|
this.Refresh();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Calculates the new value of the scrollbar.
|
|
/// </summary>
|
|
/// <param name="smallIncrement">true for a small change, false otherwise.</param>
|
|
/// <param name="up">true for up movement, false otherwise.</param>
|
|
/// <returns>The new scrollbar value.</returns>
|
|
private int GetValue(bool smallIncrement, bool up)
|
|
{
|
|
int newValue;
|
|
|
|
// calculate the new value of the scrollbar
|
|
// with checking if new value is in bounds (min/max)
|
|
if (up)
|
|
{
|
|
newValue = this.value - (smallIncrement ? this.smallChange : this.largeChange);
|
|
|
|
if (newValue < this.minimum)
|
|
{
|
|
newValue = this.minimum;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
newValue = this.value + (smallIncrement ? this.smallChange : this.largeChange);
|
|
|
|
if (newValue > this.maximum)
|
|
{
|
|
newValue = this.maximum;
|
|
}
|
|
}
|
|
|
|
return newValue;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Calculates the new thumb position.
|
|
/// </summary>
|
|
/// <returns>The new thumb position.</returns>
|
|
private int GetThumbPosition()
|
|
{
|
|
int pixelRange, arrowSize;
|
|
|
|
if (this.orientation == ScrollBarOrientation.Vertical)
|
|
{
|
|
pixelRange = this.Height - (2 * this.arrowHeight) - this.thumbHeight;
|
|
arrowSize = this.arrowHeight;
|
|
}
|
|
else
|
|
{
|
|
pixelRange = this.Width - (2 * this.arrowWidth) - this.thumbWidth;
|
|
arrowSize = this.arrowWidth;
|
|
}
|
|
|
|
int realRange = this.maximum - this.minimum;
|
|
float perc = 0f;
|
|
|
|
if (realRange != 0)
|
|
{
|
|
perc = ((float)this.value - (float)this.minimum) / (float)realRange;
|
|
}
|
|
|
|
return Math.Max(this.thumbTopLimit, Math.Min(
|
|
this.thumbBottomLimitTop,
|
|
Convert.ToInt32((perc * pixelRange) + arrowSize)));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Calculates the height of the thumb.
|
|
/// </summary>
|
|
/// <returns>The height of the thumb.</returns>
|
|
private int GetThumbSize()
|
|
{
|
|
int trackSize =
|
|
this.orientation == ScrollBarOrientation.Vertical ?
|
|
this.Height - (2 * this.arrowHeight) : this.Width - (2 * this.arrowWidth);
|
|
|
|
if (this.maximum == 0 || this.largeChange == 0)
|
|
{
|
|
return trackSize;
|
|
}
|
|
|
|
float newThumbSize = ((float)this.largeChange * (float)trackSize) / (float)this.maximum;
|
|
|
|
return Convert.ToInt32(Math.Min((float)trackSize, Math.Max(newThumbSize, 10f)));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Enables the timer.
|
|
/// </summary>
|
|
private void EnableTimer()
|
|
{
|
|
// if timer is not already enabled - enable it
|
|
if (!this.progressTimer.Enabled)
|
|
{
|
|
this.progressTimer.Interval = 600;
|
|
this.progressTimer.Start();
|
|
}
|
|
else
|
|
{
|
|
// if already enabled, change tick time
|
|
this.progressTimer.Interval = 10;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stops the progress timer.
|
|
/// </summary>
|
|
private void StopTimer()
|
|
{
|
|
this.progressTimer.Stop();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Changes the position of the thumb.
|
|
/// </summary>
|
|
/// <param name="position">The new position.</param>
|
|
private void ChangeThumbPosition(int position)
|
|
{
|
|
if (this.orientation == ScrollBarOrientation.Vertical)
|
|
{
|
|
this.thumbRectangle.Y = position;
|
|
}
|
|
else
|
|
{
|
|
this.thumbRectangle.X = position;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Controls the movement of the thumb.
|
|
/// </summary>
|
|
/// <param name="enableTimer">true for enabling the timer, false otherwise.</param>
|
|
private void ProgressThumb(bool enableTimer)
|
|
{
|
|
int scrollOldValue = this.value;
|
|
ScrollEventType type = ScrollEventType.First;
|
|
int thumbSize, thumbPos;
|
|
|
|
if (this.orientation == ScrollBarOrientation.Vertical)
|
|
{
|
|
thumbPos = this.thumbRectangle.Y;
|
|
thumbSize = this.thumbRectangle.Height;
|
|
}
|
|
else
|
|
{
|
|
thumbPos = this.thumbRectangle.X;
|
|
thumbSize = this.thumbRectangle.Width;
|
|
}
|
|
|
|
// arrow down or shaft down clicked
|
|
if (this.bottomArrowClicked || (this.bottomBarClicked && (thumbPos + thumbSize) < this.trackPosition))
|
|
{
|
|
type = this.bottomArrowClicked ? ScrollEventType.SmallIncrement : ScrollEventType.LargeIncrement;
|
|
|
|
this.value = this.GetValue(this.bottomArrowClicked, false);
|
|
|
|
if (this.value == this.maximum)
|
|
{
|
|
this.ChangeThumbPosition(this.thumbBottomLimitTop);
|
|
|
|
type = ScrollEventType.Last;
|
|
}
|
|
else
|
|
{
|
|
this.ChangeThumbPosition(Math.Min(this.thumbBottomLimitTop, this.GetThumbPosition()));
|
|
}
|
|
}
|
|
else if (this.topArrowClicked || (this.topBarClicked && thumbPos > this.trackPosition))
|
|
{
|
|
type = this.topArrowClicked ? ScrollEventType.SmallDecrement : ScrollEventType.LargeDecrement;
|
|
|
|
// arrow up or shaft up clicked
|
|
this.value = this.GetValue(this.topArrowClicked, true);
|
|
|
|
if (this.value == this.minimum)
|
|
{
|
|
this.ChangeThumbPosition(this.thumbTopLimit);
|
|
|
|
type = ScrollEventType.First;
|
|
}
|
|
else
|
|
{
|
|
this.ChangeThumbPosition(Math.Max(this.thumbTopLimit, this.GetThumbPosition()));
|
|
}
|
|
}
|
|
else if (!((this.topArrowClicked && thumbPos == this.thumbTopLimit) || (this.bottomArrowClicked && thumbPos == this.thumbBottomLimitTop)))
|
|
{
|
|
this.ResetScrollStatus();
|
|
|
|
return;
|
|
}
|
|
|
|
if (scrollOldValue != this.value)
|
|
{
|
|
this.OnScroll(new ScrollEventArgs(type, scrollOldValue, this.value, this.scrollOrientation));
|
|
|
|
this.Invalidate(this.channelRectangle);
|
|
|
|
if (enableTimer)
|
|
{
|
|
this.EnableTimer();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (this.topArrowClicked)
|
|
{
|
|
type = ScrollEventType.SmallDecrement;
|
|
}
|
|
else if (this.bottomArrowClicked)
|
|
{
|
|
type = ScrollEventType.SmallIncrement;
|
|
}
|
|
|
|
this.OnScroll(new ScrollEventArgs(type, this.value));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Changes the displayed text of the context menu items dependent of the current <see cref="ScrollBarOrientation"/>.
|
|
/// </summary>
|
|
private void ChangeContextMenuItems()
|
|
{
|
|
if (this.orientation == ScrollBarOrientation.Vertical)
|
|
{
|
|
this.tsmiTop.Text = "Top";
|
|
this.tsmiBottom.Text = "Bottom";
|
|
this.tsmiLargeDown.Text = "Page down";
|
|
this.tsmiLargeUp.Text = "Page up";
|
|
this.tsmiSmallDown.Text = "Scroll down";
|
|
this.tsmiSmallUp.Text = "Scroll up";
|
|
this.tsmiScrollHere.Text = "Scroll here";
|
|
}
|
|
else
|
|
{
|
|
this.tsmiTop.Text = "Left";
|
|
this.tsmiBottom.Text = "Right";
|
|
this.tsmiLargeDown.Text = "Page left";
|
|
this.tsmiLargeUp.Text = "Page right";
|
|
this.tsmiSmallDown.Text = "Scroll right";
|
|
this.tsmiSmallUp.Text = "Scroll left";
|
|
this.tsmiScrollHere.Text = "Scroll here";
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region context menu methods
|
|
|
|
/// <summary>
|
|
/// Initializes the context menu.
|
|
/// </summary>
|
|
private void InitializeComponent()
|
|
{
|
|
this.components = new System.ComponentModel.Container();
|
|
this.contextMenu = new System.Windows.Forms.ContextMenuStrip(this.components);
|
|
this.tsmiScrollHere = new System.Windows.Forms.ToolStripMenuItem();
|
|
this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
|
|
this.tsmiTop = new System.Windows.Forms.ToolStripMenuItem();
|
|
this.tsmiBottom = new System.Windows.Forms.ToolStripMenuItem();
|
|
this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator();
|
|
this.tsmiLargeUp = new System.Windows.Forms.ToolStripMenuItem();
|
|
this.tsmiLargeDown = new System.Windows.Forms.ToolStripMenuItem();
|
|
this.toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator();
|
|
this.tsmiSmallUp = new System.Windows.Forms.ToolStripMenuItem();
|
|
this.tsmiSmallDown = new System.Windows.Forms.ToolStripMenuItem();
|
|
this.contextMenu.SuspendLayout();
|
|
this.SuspendLayout();
|
|
//
|
|
// contextMenu
|
|
//
|
|
this.contextMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
|
this.tsmiScrollHere,
|
|
this.toolStripSeparator1,
|
|
this.tsmiTop,
|
|
this.tsmiBottom,
|
|
this.toolStripSeparator2,
|
|
this.tsmiLargeUp,
|
|
this.tsmiLargeDown,
|
|
this.toolStripSeparator3,
|
|
this.tsmiSmallUp,
|
|
this.tsmiSmallDown});
|
|
this.contextMenu.Name = "contextMenu";
|
|
this.contextMenu.Size = new System.Drawing.Size(151, 176);
|
|
//
|
|
// tsmiScrollHere
|
|
//
|
|
this.tsmiScrollHere.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
|
|
this.tsmiScrollHere.Name = "tsmiScrollHere";
|
|
this.tsmiScrollHere.Size = new System.Drawing.Size(150, 22);
|
|
this.tsmiScrollHere.Text = "Scroll here";
|
|
this.tsmiScrollHere.Click += ScrollHereClick;
|
|
//
|
|
// toolStripSeparator1
|
|
//
|
|
this.toolStripSeparator1.Name = "toolStripSeparator1";
|
|
this.toolStripSeparator1.Size = new System.Drawing.Size(147, 6);
|
|
//
|
|
// tsmiTop
|
|
//
|
|
this.tsmiTop.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
|
|
this.tsmiTop.Name = "tsmiTop";
|
|
this.tsmiTop.Size = new System.Drawing.Size(150, 22);
|
|
this.tsmiTop.Text = "Top";
|
|
this.tsmiTop.Click += this.TopClick;
|
|
//
|
|
// tsmiBottom
|
|
//
|
|
this.tsmiBottom.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
|
|
this.tsmiBottom.Name = "tsmiBottom";
|
|
this.tsmiBottom.Size = new System.Drawing.Size(150, 22);
|
|
this.tsmiBottom.Text = "Bottom";
|
|
this.tsmiBottom.Click += this.BottomClick;
|
|
//
|
|
// toolStripSeparator2
|
|
//
|
|
this.toolStripSeparator2.Name = "toolStripSeparator2";
|
|
this.toolStripSeparator2.Size = new System.Drawing.Size(147, 6);
|
|
//
|
|
// tsmiLargeUp
|
|
//
|
|
this.tsmiLargeUp.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
|
|
this.tsmiLargeUp.Name = "tsmiLargeUp";
|
|
this.tsmiLargeUp.Size = new System.Drawing.Size(150, 22);
|
|
this.tsmiLargeUp.Text = "Page up";
|
|
this.tsmiLargeUp.Click += this.LargeUpClick;
|
|
//
|
|
// tsmiLargeDown
|
|
//
|
|
this.tsmiLargeDown.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
|
|
this.tsmiLargeDown.Name = "tsmiLargeDown";
|
|
this.tsmiLargeDown.Size = new System.Drawing.Size(150, 22);
|
|
this.tsmiLargeDown.Text = "Page down";
|
|
this.tsmiLargeDown.Click += this.LargeDownClick;
|
|
//
|
|
// toolStripSeparator3
|
|
//
|
|
this.toolStripSeparator3.Name = "toolStripSeparator3";
|
|
this.toolStripSeparator3.Size = new System.Drawing.Size(147, 6);
|
|
//
|
|
// tsmiSmallUp
|
|
//
|
|
this.tsmiSmallUp.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
|
|
this.tsmiSmallUp.Name = "tsmiSmallUp";
|
|
this.tsmiSmallUp.Size = new System.Drawing.Size(150, 22);
|
|
this.tsmiSmallUp.Text = "Scroll up";
|
|
this.tsmiSmallUp.Click += this.SmallUpClick;
|
|
//
|
|
// tsmiSmallDown
|
|
//
|
|
this.tsmiSmallDown.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
|
|
this.tsmiSmallDown.Name = "tsmiSmallDown";
|
|
this.tsmiSmallDown.Size = new System.Drawing.Size(150, 22);
|
|
this.tsmiSmallDown.Text = "Scroll down";
|
|
this.tsmiSmallDown.Click += this.SmallDownClick;
|
|
this.contextMenu.ResumeLayout(false);
|
|
this.ResumeLayout(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Context menu handler.
|
|
/// </summary>
|
|
/// <param name="sender">The sender.</param>
|
|
/// <param name="e">The event arguments.</param>
|
|
private void ScrollHereClick(object sender, EventArgs e)
|
|
{
|
|
int thumbSize, thumbPos, arrowSize, size;
|
|
|
|
if (this.orientation == ScrollBarOrientation.Vertical)
|
|
{
|
|
thumbSize = this.thumbHeight;
|
|
arrowSize = this.arrowHeight;
|
|
size = this.Height;
|
|
|
|
this.ChangeThumbPosition(Math.Max(this.thumbTopLimit, Math.Min(this.thumbBottomLimitTop, this.trackPosition - (this.thumbRectangle.Height / 2))));
|
|
|
|
thumbPos = this.thumbRectangle.Y;
|
|
}
|
|
else
|
|
{
|
|
thumbSize = this.thumbWidth;
|
|
arrowSize = this.arrowWidth;
|
|
size = this.Width;
|
|
|
|
this.ChangeThumbPosition(Math.Max(this.thumbTopLimit, Math.Min(this.thumbBottomLimitTop, this.trackPosition - (this.thumbRectangle.Width / 2))));
|
|
|
|
thumbPos = this.thumbRectangle.X;
|
|
}
|
|
|
|
int pixelRange = size - (2 * arrowSize) - thumbSize;
|
|
float perc = 0f;
|
|
|
|
if (pixelRange != 0)
|
|
{
|
|
perc = (float)(thumbPos - arrowSize) / (float)pixelRange;
|
|
}
|
|
|
|
int oldValue = this.value;
|
|
|
|
this.value = Convert.ToInt32((perc * (this.maximum - this.minimum)) + this.minimum);
|
|
|
|
this.OnScroll(new ScrollEventArgs(ScrollEventType.ThumbPosition, oldValue, this.value, this.scrollOrientation));
|
|
|
|
this.Refresh();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Context menu handler.
|
|
/// </summary>
|
|
/// <param name="sender">The sender.</param>
|
|
/// <param name="e">The event arguments.</param>
|
|
private void TopClick(object sender, EventArgs e)
|
|
{
|
|
this.Value = this.minimum;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Context menu handler.
|
|
/// </summary>
|
|
/// <param name="sender">The sender.</param>
|
|
/// <param name="e">The event arguments.</param>
|
|
private void BottomClick(object sender, EventArgs e)
|
|
{
|
|
this.Value = this.maximum;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Context menu handler.
|
|
/// </summary>
|
|
/// <param name="sender">The sender.</param>
|
|
/// <param name="e">The event arguments.</param>
|
|
private void LargeUpClick(object sender, EventArgs e)
|
|
{
|
|
this.Value = this.GetValue(false, true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Context menu handler.
|
|
/// </summary>
|
|
/// <param name="sender">The sender.</param>
|
|
/// <param name="e">The event arguments.</param>
|
|
private void LargeDownClick(object sender, EventArgs e)
|
|
{
|
|
this.Value = this.GetValue(false, false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Context menu handler.
|
|
/// </summary>
|
|
/// <param name="sender">The sender.</param>
|
|
/// <param name="e">The event arguments.</param>
|
|
private void SmallUpClick(object sender, EventArgs e)
|
|
{
|
|
this.Value = this.GetValue(true, true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Context menu handler.
|
|
/// </summary>
|
|
/// <param name="sender">The sender.</param>
|
|
/// <param name="e">The event arguments.</param>
|
|
private void SmallDownClick(object sender, EventArgs e)
|
|
{
|
|
this.Value = this.GetValue(true, false);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
}
|
|
} |