Introduction
In one of my recent projects, I needed to scroll large number of items. To be precise, I wanted to scroll contents of entire physical drive. It is easy to guess that out-of-the-box WinForms ScrollBar
with Maximum
, Minimum
, and Value
properties defined as Integer (Int32
) is not very useful for this purpose.
In the reality, limitation is even stronger. In design mode: setting Maximum
to anything more than 1,000,000,000 and Minimum
to anything less than -1,000,000,000 results in design time exception ("Property value is not valid" message). On the other hand, you can set Minimum
and Maximum
from the code to any valid integer (Int32
) value. No exception is thrown. But when expression Maximum
- Minimum
evaluates to more than int.MaxValue -1,
you can observe strange behavior. Value of Maximum
suddenly changes to something different than you wanted it to be. Better don't even try to play with Microsoft's ScrollBar
and big numbers. For this, you have ScrollBarEnhanced
described in this article.
As I said above:
- I wanted
ScrollBar
to accept at least long
values for Maximum
, Minimum
and Value
properties.
- I also wanted to be able to bookmark certain
ScrollBar
values and be able to see bookmarked values on the ScrollBar
itself as colored little dots, squares or bars.
- And finally, I wanted the ability to display dynamic tooltips when mouse moves over bookmark or over not marked track area.
If you remember Vertical ScrollBar
in Visual Studio with multicolored graphical decorations, this is what I wanted to achieve.
Background
A quick search of CodeProject revealed a great number of Scrollbar
related articles but none of them fully addressed my needs.
In the first attempt to solve my problem, I built a custom control inherited from VScrollBar
(I didn't care about the horizontal scrollbar at this time, all I needed was vertical scrollbar). I was able to overwrite Maximum
, Minimum
, and Value
properties to accept long
values. But first, it was quite ugly code (translating long
values from the wrapper to int
values for underlying VScrollBar
and vice versa and I still could not show a graphical decoration for bookmarks, and dynamic tooltips were not easy to implement as well.
What inspired me to write my own ScrollBar
(I decided to call it ScrollBarEnhanced
) was the CodeProject article by Greg Ellis How to skin scrollbars for Panels, in C#. This article showed to me that building a ScrollBar
from scratch is not so difficult and that writing my own ScrollBar
gives me the way for even more customization and more functionality if I ever needed it.
After I decided to code ScrollBarEnhanced
from scratch, I quickly upgraded my requirements to use decimal
instead of long
for Maximum
, Minimum
, SmallChage
, LargeChange
and Value
properties.
Decimal
has 28-29 significant digit precision and an approximate values range of ±1.0 × 10-28 to ±7.9 × 1028. This is much better than long
with values in range –9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 ("only" 19 significant digits). There is the reason for greater accuracy of decimal
, decimal
is 128-bit data type while long
is 64-bit "only". And the final argument for using decimal
was fact that WPF version of ScrollBar
does use decimal
so why not have similar behavior in WinForms version of it.
To code my own ScrollBarEnhanced
implementation, I need to program the following:
- Drawing of scrollbar's elements (arrows, thumb, trackbar, and grip). It turned out be very easy by using
ScrollBarRenderer
static
methods of ScrollBar
control. All drawing should be coded in overwritten OnPaint
handler.
- Drawing of bookmark markers graphical representation also in
OnPaint
handler. Not difficult as well as I just needed to draw rectangles (for squares and bars) and ovals (for dots).
- Detecting mouse movements and clicks (in
OnMouseMove
, OnMouseDown
, OnMouseUp
, OnMouseLeave
and OnMouseClick
handlers) and define few enhanced events to pass information back the host.
- Define
Maximum
, Minimum
. SmallChage
, and LargeChage
and Value
properties as decimal.
Overview
For start, I created in Visual Studio ScrollBarEnhanced
custom control inherited from UserControl
.
public class ScrollbarEnhanced : UserControl
This control encapsulates functionality of both Vertical
and Horizontal ScrollBar
and adds several enhancements to satisfy my needs.
All drawing happens in OnPaint
. Below I'm showing simplified version that paints basic ScrollBar
. The "real" code has a little bit more lines in order to address different way of painting hot or pressed elements of ScrollBar
. I removed them here to show how simple is the basic drawing of ScrollBarEnhanced
: just few calls to ScrollBarRenderer
draw methods - that's all.
protected override void OnPaint(PaintEventArgs e)
{
int ArrowHeight = SystemInformation.VerticalScrollBarArrowHeight;
ScrollBarRenderer.DrawArrowButton(e.Graphics,rec, ScrollBarArrowButtonState.UpNormal);
int ThumbPos = Value2ThumbTopPosition(Value);
rec = new Rectangle(0, ArrowHeight + 1, ClientSize.Width, ThumbPos - ArrowHeight);
ScrollBarRenderer.DrawUpperVerticalTrack(e.Graphics, rec, ScrollBarState.Normal);
int nThumbHeight =ThumbHeight;
rec = new Rectangle(0, ThumbPos, ClientSize.Width, nThumbHeight);
ScrollBarRenderer.DrawVerticalThumb(e.Graphics, rec, ScrollBarState.Normal);
ScrollBarRenderer.DrawVerticalThumbGrip(e.Graphics, rec, ScrollBarState.Normal);
rec = new Rectangle(0, ThumbPos + nThumbHeight,
ClientSize.Width, TrackHeight - (ThumbPos + nThumbHeight));
ScrollBarRenderer.DrawLowerVerticalTrack(e.Graphics, rec, ScrollBarState.Normal);
rec = new Rectangle(0, ClientSize.Height - ArrowHeight, ClientSize.Width, ArrowHeight);
ScrollBarRenderer.DrawArrowButton(e.Graphics, rec, ScrollBarArrowButtonState.DownNormal);
if (Bookmarks != null)
{
foreach (ScrollBarBookmark bk in Bookmarks)
{
DrawBookmark(e.Graphics, bk);
}
}
}
DrawBookmark
method called from OnPaint
is used to display bookmark markers on the ScrollBarEnhanced
control. They are three flavors of bookmarks: ImageScrollBarBookmark,
BasicShapeScrollBarBookamrk
and ValueRangeScrollBarBookmark
. All three are derived from Abstract
class ScrollBarBoookmark
.
ImageScrollBarBookmark
as the name indicates is a bookmark drawn as a small image. The center of the image is located at the Value property of the bookmark.
BasicShapeScrollBarBookmark
is a colored rectangle or square drawn over scrollbar
body. The center of the shape is located in the Value property of the bookmark.
ValueRangeScrollBarBookmark
is a variation of BasicShapeScrollBarBookmark
. But while former two bookmarks are "pont bookmarks": they are associated with exact value of scrollbar, ValueRangeScrollBarBookmark
is bookmark associated with range of values. It has one extra property EndValue
, that together with Value defines range. ValueRangeScrollBarBookmark
could be used to mark range of elements scrollbar is associated with. For example if you want to use ScrollBarEnhanced
to scroll lines of text in text editor, you can use range scrollbar to mark selected text lines. Screenshot from demo programs has a sample of two ValuRangeScrollBarBookmars
: one is drawn as right justified orange line and and another one as green (lime), left justified line.
private void DrawBookmark(Graphics Graphics, ScrollBarBookmark Bookmark)
{
BasicShapeScrollBarBookmark shapeBookmark = null;
ImageScrollBarBookmark imageBookmark=null;
if (Bookmark is BasicShapeScrollBarBookmark)
shapeBookmark = (BasicShapeScrollBarBookmark)Bookmark;
else
imageBookmark = (ImageScrollBarBookmark)Bookmark;
if (shapeBookmark != null)
{
if (shapeBookmark.Brush == null) {
if (!m_BrushesCache.ContainsKey
(shapeBookmark.Color)) m_BrushesCache.Add(shapeBookmark.Color, new SolidBrush(shapeBookmark.Color));
shapeBookmark.Brush = m_BrushesCache[shapeBookmark.Color];
}
if (shapeBookmark.Pen == null) {
if (!m_PensCache.ContainsKey
(shapeBookmark.Color)) {
m_PensCache.Add(shapeBookmark.Color, new Pen(shapeBookmark.Color));
}
shapeBookmark.Pen = m_PensCache[shapeBookmark.Color];
}
if (shapeBookmark.Stretch)
{
Bookmark.X = 0;
shapeBookmark.Width = this.ClientSize.Width;
}
else
{
CalculateBookmarkXPosition(Bookmark);
}
}
else {
CalculateBookmarkXPosition(Bookmark);
}
Bookmark.Y = (int)(TrackHeight * Bookmark.Value / Maximum +
SystemInformation.VerticalScrollBarArrowHeight - Bookmark.Height / 2);
if (imageBookmark != null)
{
Graphics.DrawImage(imageBookmark.Image, new Point(Bookmark.X, Bookmark.Y));
}
else if (shapeBookmark.FillBookmarkShape)
{
if (shapeBookmark.Shape == ScrollbarBookmarkShape.Oval)
Graphics.FillEllipse(shapeBookmark.Brush, new Rectangle(Bookmark.X,
Bookmark.Y, shapeBookmark.Width, shapeBookmark.Height));
else
Graphics.FillRectangle(shapeBookmark.Brush, new Rectangle(Bookmark.X,
Bookmark.Y, shapeBookmark.Width, shapeBookmark.Height));
}
else
{
if (shapeBookmark.Shape == ScrollbarBookmarkShape.Oval)
Graphics.DrawEllipse(shapeBookmark.Pen, new Rectangle(Bookmark.X,
Bookmark.X, shapeBookmark.Width, shapeBookmark.Height));
else
Graphics.DrawRectangle(shapeBookmark.Pen, new Rectangle(Bookmark.X,
Bookmark.X, shapeBookmark.Width, shapeBookmark.Height));
}
}
private void CalculateBookmarkXPosition(ScrollBarBookmark Bookmark)
{
switch (Bookmark.Alignment)
{
case ScrollBarBookmarkAlignment.Left:
Bookmark.X = 0;
break;
case ScrollBarBookmarkAlignment.Right:
Bookmark.X = this.ClientSize.Width - Bookmark.Width;
break;
case ScrollBarBookmarkAlignment.Center:
Bookmark.X = (this.ClientSize.Width - Bookmark.Width) / 2;
break;
}
}
ScrollBarBookmark
class mentioned above requires a little bit more attention. You will be using only if you want to display bookmarks on your scrollbar. ScrollBarBookmark
is an abstract
class that contains basic bookmark properties defined as follows:
public abstract class ScrollBarBookmark
{
[DefaultValue(0)]
public decimal Value { set; get; }
[DefaultValue(ScrollBarBookmarkAlignment.Left)]
public ScrollBarBookmarkAlignment Alignment { set; get; }
[DefaultValue(null)]
public object Tag { set; get; }
public virtual int Height { set; get; }
public abstract int Width { set; get; }
protected internal int X { set; get; }
protected internal int Y { set; get; }
}
"Real" bookmarks is stored in instance of one of three classes inherited from ScrollBarBookmark
:
ImageScrollBarBookmark
- This class allows to define bookmark with Image as its graphical representation
BasicShapeScrollBarBookmark
- This class allows to define bookmark as basic shapes (rectangles or ovals)
ValueRangeScrollBarBookmark
- Technically this class id derived from BasicShapeScrollBarBookmark
and it has only one extra property defined: EndRange
. It allows to draw bookmark not as a point at provided value but as a range. Typically you will use rectangle shape for this purpose.
ImageScrollBarBookmark
is defined below:
public class ImageScrollBarBookmark : ScrollBarBookmark
{
public ImageScrollBarBookmark()
{
this.Image = null;
this.Value = 0;
this.Alignment = ScrollBarBookmarkAlignment.Left;
this.Tag = null;
}
public ImageScrollBarBookmark(decimal Value, Image Image,
ScrollBarBookmarkAlignment Alignment, object Tag)
{
this.Image = Image;
this.Value = Value;
this.Alignment = Alignment;
this.Tag = Tag;
}
public Image Image { set; get; }
public override int Width
{
get { return Image.Width; }
set { ;}
}
public override int Height
{
get { return Image.Height; }
set {;}
}
}
And here goes BasicShapeScrollBarBookmark
:
public BasicShapeScrollBarBookmark()
{
Value = 0;
Alignment = ScrollBarBookmarkAlignment.Left;
Height = 5;
Width = 5;
Shape = ScrollbarBookmarkShape.Rectangle;
Color = Color.Orange;
FillBookmarkShape = true;
Stretch = false;
Tag = null;
}
public BasicShapeScrollBarBookmark(decimal ScrollValue,
ScrollBarBookmarkAlignment Alignment, int Height,
int Width, bool Stretch, object Tag)
{
this.Value = ScrollValue;
this.Alignment = Alignment;
this.Height = Height;
this.Width = Width;
this.Stretch = Stretch; this.Tag = Tag;
this.Shape = ScrollbarBookmarkShape.Rectangle;
this.Color = Color.Empty;
this.FillBookmarkShape = true;
}
public BasicShapeScrollBarBookmark(decimal ScrollValue,
ScrollBarBookmarkAlignment Position, int Height, int Width,
ScrollbarBookmarkShape Shape, Color Color, bool FillBookmarkShape,
bool StretchToScrollBarWidth, object Tag)
{
this.Value = ScrollValue;
this.Alignment = Position;
this.Height = Height;
this.Width = Width;
this.Shape = Shape;
this.Color = Color;
this.FillBookmarkShape = FillBookmarkShape;
this.Stretch = StretchToScrollBarWidth;
this.Tag = Tag;
}
int m_Width = 0;
[DefaultValue(4)]
public override int Width
{
set { m_Width = value; }
get { return m_Width; }
}
int m_Height = 0;
[DefaultValue(4)]
public override int Height
{
set { m_Height = value; }
get { return m_Height; }
}
[DefaultValue(false)]
public bool Stretch { set; get; }
[DefaultValue(ScrollbarBookmarkShape.Rectangle)]
public ScrollbarBookmarkShape Shape { set; get; }
[DefaultValue(true)]
public bool FillBookmarkShape { set; get; }
private Color m_Color = Color.Empty;
[DefaultValue(typeof(Color), "Orange")]
public Color Color
{
set
{
if (m_Color != value)
{
m_Color = value;
Pen = null;
Brush = null;
}
}
get { return m_Color; }
}
[NonSerialized]
private Pen m_Pen;
protected internal Pen Pen
{
set { m_Pen = value; }
get { return m_Pen; }
}
[NonSerialized]
private Brush m_Brush;
protected internal Brush Brush
{
set { m_Brush = value; }
get { return m_Brush; }
}
ValueRangeScrollBarBookmark
adds one extra property to BasicShapeScrollBarbookmark
: EndValue.
public class ValueRangeScrollBarBookmark : BasicShapeScrollBarBookmark
{
public ValueRangeScrollBarBookmark()
{
Name = "";
Value = 0;
EndValue = 0;
Alignment = ScrollBarBookmarkAlignment.Left;
Height = 5;
Width = 5;
Shape = ScrollbarBookmarkShape.Rectangle;
Color = Color.Orange;
FillBookmarkShape = true;
Stretch = false;
Tag = null;
}
public ValueRangeScrollBarBookmark(string Name, decimal StartValue,
decimal EndValue, ScrollBarBookmarkAlignment Alignment, int Depth,
Color Color, bool FillBookmarkShape, bool StretchToScrollBarWidth, object Tag):
base(Name, StartValue, Alignment, 0, Depth, ScrollbarBookmarkShape.Rectangle,
Color, FillBookmarkShape, StretchToScrollBarWidth, Tag)
{
this.EndValue = EndValue;
}
public decimal EndValue { set; get; }
int m_Width = 0;
[DefaultValue(4)]
[Description("ValueRangeScrollBarBookmark Height (for vertical)
or Width (for horizontal) are calculated every time scrollbar is repainted.
Value will change every time Minimum, Maximum or scrollbar size change.")]
public override int Width
{
set { m_Width = value; }
get { return m_Width; }
}
int m_Height = 0;
[DefaultValue(4)]
[Description("ValueRangeScrollBarBookmark Height (for vertical)
or Width (for horizontal) are calculated every time scrollbar is repainted.
Value will change every time Minimum, Maximum or scrollbar size chage.")]
public override int Height
{
set { m_Height = value; }
get { return m_Height; }
}
}
When ValueRangeScrollBookmark
is drawn in OnPaint
method its length (height for Vertical or width for horizontal scrollbar) is calculated:
int bookmarkLength =(Bookmark.EndValue - Bookmark.Value) / (Maximum - Minimum)) * TrackLength);
And finally goes definition of enums
used in above code:
public enum ScrollbarBookmarkShape
{
Rectangle,
Oval,
}
public enum ScrollBarBookmarkAlignment
{
Left,
Right,
Center
}
Typically, you would assign bookmarks from the code. For example, user of your program clicks something on the page with attached scrollbar and wants to mark this position for quick reference.
Here is the short description of BasicShapeScrollBarBookmark properties
Value
- Value property of ScrollBarEnhanced
corresponding to the center of bookmark graphical representation.
Height
-Height of the bookmark graphical element; default value is 5 pixels.
Width
- Width of the bookmark graphical element; default value is 5 pixels.
Stretch
- If this property is set to true, Width and Position properties are disregarded and bookmark is shown as a rectangle or oval (depending on Shape
property value) with Width equal to the width of the scrollbar control instance.
Shape
- Bookmark shape enumerator: can be Rectangle or Oval
FillBookmarkShape
- If set to true, bookmark body is filled with the same color as the bookmark border, otherwise only border line of bookmark is displayed. Since bookmark markers are usually small, most likely you would like to keep this property set to true to make marker more visible.
Color
- Bookmark color; color used to draw bookmark border and the body.
Alignment
- Bookmark position enumerator. Can be Left, Center, or Right
Tag
- Anything can go here. It can be useful if you want to assign more information to the bookmark object than list of predefined properties can offer. ScrollBarEnhanced
doesn't make any use if it, but since some enhanced events return original bookmark object to the host (for example, list of bookmarks mouse is moving over), host can find Tag values handy in some situations.
ImageScrollBarBookmark has a little bit different set of properties:
Value
- Value property of ScrollBarEnhanced
corresponding to the center of bookmark graphical representation.
Image
- Graphical element to be displayed as bookmark.
Height
- Height of the bookmark graphical element (height of the Image). This is read-only property.
Width
- Width of the bookmark graphical element (width of the Image). This is read-only property.
Alignment
- Bookmark position enumerator. Can be Left, Center, or Right
Tag
- Anything can go here. It can be useful if you want to assign more information to the bookmark object than list of predefined properties can offer. ScrollBarEnhanced
doesn't make any use if it, but since some enhanced events return original bookmark object to the host (for example list of bookmarks mouse is moving over), host can find Tag
values handy in some situations.
ValueRangeScrollBarBookmark has identical properties as BasicShapeScrollBarBookmark plus one extra:
EndValue
- Together with Value
it defines range of values and effectively the size of shape used to draw the bookmark.
Besides OnPaint
few more methods play key role in the code. Below they are briefly mentioned. If you want more details please view ScrollBarEnhanced
source code attached to this article.
OnMouseDown
- saves current mouse position. Fires proper OnScroll
events. If mouse down was on up or down arrow, decrements or increments Value
by SmallChange
amount. If mouse was down on upper or lower track area, decrements or increments Value
by LargeChage
.
OnMouseMove
- If mouse is down and over the thumb, performs thumb dragging. Otherwise fires MouseMove
event with extended list values passed as an argument.
OnMouseUp
-Fires Scroll event with extended list values passed as an argument.
OnMouseClick
- Fires MouseClick
event with extended list values passed as an argument.
OnMouseLeave
- Cleanups after other mouse actions.
Using the Code
You can use ScrollBarEnhanced
control exactly same way you are using regular WinForms VScrollBar or HScrollBar. ScrollBarEnhanced
has an Orientation
property that makes scrollbar to behave like vertical or horizontal scrollbar appropriately. You just have to drop it on desired Form, set up needed properties, hook up events you want to intercept and you are ready to go.
Enhanced Properties You Can Use Are:
Bookmarks
- Collection of ScrolBarBookmark
objects. Each element defines where and how to display bookmark graphical marker. You can define value, location, size, color and shape.
BookmarksOnTop
- This property defines if bookmark are drawn as the topmost items (when set to true). This means that scrollbar thumb might be partially cover with bookmark. If this property is set to false, topmost item is scrollbar thumb.
EndValue
- Together with Value
property it defines range for which scrollbar is defined. EndValue
is defined of ValueRangeScrollBarBookmark
only.
ContextMenuStrip
- This property is inherited from, the base control but unlike context menu of standard VScrollBar and HSrollBar is fully customizable. You can intercept Opening events and modify context menu before display or simply replace context menu with your own.
InitialDelay
-Standard VScrollBar has one cool feature I wanted to reproduce. When you keep the left mouse button in down position it repeats click action without you actually clicking. This behavior starts after holding mouse down for interval defined in milliseconds in InitialDelay
property. Value of InitialDelay
is usually bigger than RepeatInterval
. Default value is 400 milliseconds.
Orientation
- enumerator defining if ScrollBarEnhanced
behaves like vertical or horizontal scrollbar.
QuickToolbarNavigtion
- if set to true, allows quick navigation to bookmarked values by clicking bookmark graphical representation
RepeatInterval
- After auto-repeat behavior is triggered by holding mouse down for InitialDelay
milliseconds, all subsequent auto-generated clicks occur in intervals defined in RepeatInerval
. Default value is 62 milliseconds
ShowTooltipOnMouseMove
- If this property set to true, every time when user is moving mouse over scrolbar track, tooltip will be displayed. By default tooltip text shows value mouse is passing over. Tolltip text can be overwritten by host code by handling TooltipNeeded
event by changing ToolTip property of event arguments object passed to the event handler.
Value
, Minimum
, Maximum
, SmallChage
, LargeChage
- they have the same meaning as in standard VScrollBar
or HScrollBar
control but now all have type of decimal.
Enhanced Events:
There three new events defined in ScrollBarEnhanced
that are not present is standard VScrollBar
and HScrollBar
:
OrienationChanged
- fires every time Orientation property of ScrollBarEnhanced
changes.
OrientationChanging
- fires every time value of Orientation property is about to change. This is cancellable event.
ToolTipNeeded
- I wanted to be able to capture all situations when scrollbar needs to display tooltip, and be able to either use default tooltip value or change it on the fly.
Here is a short description of all events defined in ScrollBarEnhanced:
MouseClick
- This event occurs every time mouse is clicked in the scrollbar trackbar area. This event is identical to MouseClick
of VScrollBar
or HScrollBar
.
MouseMove
- This event occurs every time mouse moves over ScrollBarEnhanced
control . See EnhancedMouseEventArgs
for detailed information about argument passed to the event handler.
OrientationChanging
- This event occurs every tine orientation of ScrollBarEnhanced
is about to change. User has the opportunity to cancel change of Orientation property value.
OrientaionChange
- This notification happens every time after orientation property changes.
TooltipNeeded
- This event is fired every time mouse moves over trackbar area or over bookmark marker (in order for this event to be fired, ShowToooltipOnMouseMove
property has to be set to true). By handling this event, you can modify default tooltip value. See ToolTipNeededEventArgs
for detailed information about argument passed to the event handler.
Scroll
- Mimics Scroll
event of standard VScrollBar
and HScrollBar
. See EnhancedScrollEventArgs
for detailed information about argument passed to the event handler.
ValueChanged
- Occurs every time scrollbar value changes. This event is identical to standard VScrollBar or HScrollBar ValueChanged
event.
Enhanced Event Arguments
EnhancedMouseEventArgs
- This object is used to pass information to MouseMove and MouseClick event handlers. Please take a look at Bookmarks property. It is collection. Reason for this is the fact that one single vertical pixel of ScrollBarEnhanced can be mapped to multiple bookmarks. First, nothing stops you from defining multiple Boookmarks for the same ScrollBarEnhanced
value. Second, even if you would be protected from assigning multiple bookmarks for the same value, you still might end up with multiple bookmarks displayed over same ScrollBar pixel. Bookmarks on the list are displayed in the same order as they occur on the list of Bookmarks property. Last element is drawn last so on the display it appears to be on the top. Let's consider the following (a little bit extreme) example. You want to scroll through the entire content of physical drive of 1Tb (Terabyte) size (~1012 bytes). Let's assume that you are going to show 100 bytes in one line of display. This means that you need to set ScrollbarEnhanced
Maximum to 1012/100=1010) - that how many lines you want to scroll through. Finally, assume that your ScrollBarEnhanced control is displayed with 1000 vertical pixels on the track bar. That means that you have to map <0, 1010> ScrollBarEnhanced
address space to <0, 1000> pixels of display. Therefore, 1 pixel will be mapped to 1010/1000= 107 (10 Millions) values. So even if you set one bookmark at 0 value and another one at 5,000,000, both bookmarks will be displayed at the same point on the screen.
EnhancedScrollEventArgs
- This definition is almost identical to ScrollEventArs
used in VScrollBar. Only difference is that OldValue
and NewValue
are decimal not int.
public class EnhancedMouseEventArgs:MouseEventArgs
{
public EnhancedMouseEventArgs(decimal Value, MouseEventArgs MouseArgs,
List<ScrollBarBookmark> Bookmarks,
EnhancedScrollBarMouseLocation ScrollBarSection )
:base(MouseArgs.Button, MouseArgs.Clicks, MouseArgs.X, MouseArgs.Y,
MouseArgs.Delta)
{
this.Value = Value;
this.Bookmarks = Bookmarks;
this.ScrollBarSection = ScrollBarSection;
}
public decimal Value {private set; get;}
public List<ScrollBarBookmark> Bookmarks {private set; get;}
public EnhancedScrollBarMouseLocation ScrollBarSection { private set; get; }
}
ToolTipNeededEventArgs
- used for ToolTipNeeded
event. It passes extended information about point of the ScrollBarEnhanced
that needs tooltip. ToolTipNeededEventArgs
defines the following properties:
Value
- ScrollBarEnhanced
Value property that requires tooltip text.
ToolTip
- Default tooltip value ScrollBarEnhanced
wants to display. This value can be changed.
Bookmarks
- List of bookmarks mouse is hovering over. Last bookmark on the list represents topmost bookmark on the screen, by having this list available, you can display tooltip based on bookmark context.
public class TooltipNeededEventArgs : EventArgs
{
public TooltipNeededEventArgs(decimal Value, string ToolTip,
List<ScrollBarBookmark> Bookmarks)
{
this.Value=Value;
this.ToolTip=ToolTip;
this.Bookmarks = Bookmarks;
}
public decimal Value { private set; get; }
public string ToolTip {set; get;}
public List<ScrollBarBookmark> Bookmarks { set; get; }
}
public decimal OldValue {private set; get;}
public decimal NewValue {private set; get;}
public ScrollOrientation ScrollOrientation {private set; get;}
public ScrollEventType Type {private set; get;}
}
Notes
- I decided to "fix" default VScrollBar and HScrollBar behavior, that limits Value to range <Minimum, Maximum-LargeChange +1>. Microsoft's design assumes that
LargeChange
is equal to the size of the page you are scrolling, so when Value is equal Maximum
- LargeChange
+1 you still should be able to see all end elements of the page you are scrolling. Perhaps it makes sense, but I prefer simpler interpretation of Maximum
and no relation of Maximum to the page size. ScrollBarEnhanced
can be scrolled from Minimum to Maximum (inclusive). If you prefer default ScrollBar behavior as conceived by Microsoft engineers, you can always modify source code to support your needs.
Minimum
and Maximum
are both defined as decimal values. So theoretically, they both should accept any decimal value. While the former statement is essentially true, there is one extra constraint on Maximum
and Minimum
. Value of Maximum
- Minimum
cannot be greater than decimal.MaxValue
. For example:
Minimum = 0;
Maximum = decimal.MaxValue;
Minimum = deximal.MinValue/2+1;
Maximum = decimal.MaxValue/2;
Minimum = -1;
Maximum= decimal.MaxValue;
Demo Program
The downloadable demo program illustrates how to use ScrollBarEnhanced
control in your code. It doesn't require much explanation. From the program GUI, you can modify all properties of instance of ScrollBarEnhanced
control (scrollbar is displayed on the central part of the form). All added and modified properties are grouped in Enhanced category. I did it only to make the demo program easier to navigate since all enhanced properties will be displayed next to each other on the property grid. You can of course change property definition and change category attribute to more logical value. In fact, they all should belong to Behavior category. GUI also captures and displays all enhanced events fired by the instance of ScrollBarEnhanced
instance.
Downloadable zip file contains also help file (chm). Please note, that since you are downloading this file from the Internet, in order to use it you have to unblock it first. You can do it from Windows explorer: right_click on the help file, select 'Properties', click 'Unblock' button.
History
July 31, 2013 - Initial version of the article.
August 1, 2013 - Fixed source code bug: modification of Bookmark's Color
property didn't work due to Brush and Pen caching.
August 3, 2013 - A few changes:
- Removed
BackgroundColor
property. It was mistake to have it in the first place.
- Added new bookmark type
ImageScrollBarBookmark
- new bookmark type that allows to display tiny images (icons, bitmaps) as the bookmark graphical representation.
- Added
QuickBookmarkNavigation
property. It allows to instantaneous navigation to the bookmark value when bookmark is clicked.
August 6, 2013 - Bug fixes and functional enhancements:
- Bug fix: divide by zero exception when
Maximum
== LargeChange
was fixed
- Added
Orientation
property. This property enhances scope of entire article. Initially it was about Vertical ScrollBar enhancements, now it covers both Vertical and Horizontal ScrollBar functionality.
August 7, 2013 - Source code correction to fix the issue of incorrect drawing when Minimal value is different than zero. I also changed meaning of Value argument passed to OnMouseClick
event. Previously it contained current Value of ScrolBarEnhanced
instance. Now it is HotValue
(value under mouse position).
August 9, 2013 - Few more additions and changes:
- Added customizable context menu strip similar to context menu of
VScrollBar
and HScrollBar
.
- Added support for mouse wheel movement
- Added
BookmarksOnTop
Boolean property to control order scrollbar elements are painted.
- Changed
EnhancedMouseEventArgs
to inherit from MouseArgs
, to pass more detailed information to the event handler.
August 12, 2013 - Added ValueRangeScrollBarBookmark
that allows to create bookmarks based on range of values.
September 17th, 2013 - Changes in downloadable file: added help (chm); source code changes: added comments, corrected Dispose
override.
December 12th, 2013 - Code review and cleanup. Adding bookmarks property editor for design mode.
October 2nd, 2014 - Bug fix; Argument of Scroll event :EnhancedScrollEventArgs was showning oldValue instedad of new value and vice versa. The bug was found by altie - altie thank you for showing it to me. Source code was updated.