Introduction
I was working (in my free time) on a calendar control which looks similar to the one in Outlook 2007 and I encountered, among other problems, that the standard scrollbar doesn't fit nicely with the look of my calendar control. So I searched the Web and CodeProject for custom drawn scrollbars.
Among others, I found the article from Greg Ellis. Although it pointed me in the right direction, I missed some things the system scrollbar is capable of. As a last resort, I searched in the help of Visual Studio and found a simple example of a custom drawn scrollbar which I took as a starting point.
Some Remarks
This control is mostly useful for developers who build their own custom drawn controls and need a scrollbar.
The scrollbar has one major shortcoming - the width is fixed. I didn't need this feature and the drawing would be more complex, so I left it out.
Because I used collection initializers, the project builds only with a C# 3.0 compiler and upwards, but that can easily be fixed.
To enable the scrollbar to scroll when pressing and holding the mouse button, I used a timer - didn't find another way.
The drawing is realized with GDI+ (in a separate renderer class) with two exceptions: the little arrow in the arrow buttons and the grip in the thumb are images - there, shame on me, I was a bit lazy.
Layout of the Control
As it is said, a picture says more than thousand words, here is a diagram with the essential classes:
Please note that the property Opacity
sets the opacity of the context menu, not of the control itself.
I left out the control designer class because it's not really necessary, only when using Visual Studio.
Let's Start
When drawing a control from scratch, it's necessary to override the OnPaint
method and also, in my humble opinion, to set some control styles in the constructor:
public ScrollBarEx()
{
SetStyle(
ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw
| ControlStyles.Selectable | ControlStyles.AllPaintingInWmPaint
| ControlStyles.UserPaint, true);
....
}
After that, some thoughts have to go to the logic of the control, among them methods to calculate the thumb size and position.
As an example, here is the method that calculates the thumb size:
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)));
}
The second step was to think about mouse handling, so one needs to override the OnMouseDown
, OnMouseUp
and OnMouseMove
methods to interact with the mouse.
There are three main areas where mouse handling is done:
- arrow buttons
- thumb
- track above and under the thumb
As an addition, I've also overridden OnMouseEnter
and OnMouseLeave
to get a visual indication whether the mouse is over the scrollbar or not.
The third step was the interaction with the keyboard and can be done in several ways - one way is to override the ProcessDialogKey
method which captures also the arrow keys that will be needed to navigate the scrollbar.
The last step was to implement the drawing functionality which I put in a renderer class to separate logic and drawing of the control.
To prevent the painting but still be able to update the scrollbar, two methods were added to the control:
These methods send a message via the SendMessage
API switching the painting on or off.
Some Last Words
This is my first article here on CodeProject (or elsewhere), so please bear with me. I know this isn't a very good article, but I hope with time and experience future articles will be better. Feel free to point out what I should have done better.
History
- 28th August, 2009: Initial post