Click here to Skip to main content
Click here to Skip to main content

Cool Scrollbar - Scrollbar like Windows Media Player's

By , 3 May 2005
 

Introduction

Hans Dietrich did a good job in creating a Windows Media Player like XScrollbar; however, for most Visual Basic programmers, it's a little bit inconvenient to use that expressly in Visual Basic, so I recently wrote a VB edition of Dietrich's XScrollbar – Cool Scrollbar, using GDI+ to draw everything. It currently has:

  • Optional color displayed on thumb, in channel, and on control border.
  • Hand cursor is displayed when mouse hovers over thumb.
  • Thumb color is changed to "hover color" when mouse hovers over thumb.
  • Clicking in channel moves thumb to that spot.
  • Left and right arrow buttons move thumb by one unit, and if kept pushed, the thumb would keep moving.
  • ValueChanged event is raised when value changes.
  • Both horizontal and vertical versions of the scrollbar.

This control is still in its infantile period and many things could be added to it. I would appreciate any advice to enhance its performance.

First things first

Some introduction to GDI+

GDI+ differs from GDI in two aspects:

  1. GDI+ expands the functions of GDI by means of providing new functions (i.e., gradient brush and alpha blending).
  2. GDI+ redefines programming model to make it easier and flexible for graphics programming.

GDI+ can be used to show images, draw custom shapes and lines, draw strings, and even image transformation. In our project, we use it to draw and fill shapes, and show images.

In order to use GDI+, a Graphics object must be created and instantiated. For instance:

Dim g as Graphics = Button1.CreateGraphics

If you want to draw lines or shapes, a Pen class is needed:

Dim myPen as new Pen(Color.Red)

Then you can draw lines and shapes:

g.DrawLine(myPen, 1, 1, 45, 65) 'draw line
g.DrawBezier(myPen, 15, 15, 30, 30, 45, 30, 87, 20)  'draw Bezier curve
g.DrawEllipse(myPen, New Rectangle(33, 45, 40, 50))  'draw ellipse
g.DrawPolygon(myPen, New PointF() {New PointF(1, 1), _
   New PointF (20, 10), New PointF(5, 4), New PointF(100, 2), _
   New PointF(200, 50), New PointF(39, 45)})  'draw polygon

To fill a specific area, a Brush object is needed:

Dim myBrush as New SolidBrush(Color.Red)

In fact, several different brushes exist in GDI+, as listed below:

Brush class

Description

SolidBrush

Pure color

HatchBrush

Similar to SolidBrush but you could select the color you want from preset colors

TextureBrush

Use texture (i.e. picture)

LinearGradientBrush

Gradient

PathGradientBrush

Complex gradient

The ones we uses in our project are SolidBrush, LinearGradientBrush, and PathGradientBrush.

If you want to show an image, an Image object must be created and instantiated. For example:

Dim myBitmap as New Bitmap(System.Environment.GetFolderPath _
      (System.Environment.SpecialFolder.MyPictures))

Then you can load this image to the Graphics object in a specific location:

g.DrawImage(myBitmap, 1, 1)

Don't forget to free memory after using the Graphics and Image objects:

g.Dispose()
myBitmap.Dispose()

Please refer to GDI+ references in MSDN for details.

Nomenclature

The following image shows the components and nomenclature of a Cool Scrollbar:

Drawing Cool Scrollbar

Before I started, a list of the member variables and their meanings should be made clear. In the class CBar, I used the following member variables:

    'height of the arrow bitmap
    Private Const BitmapHeight As Byte = 12
    'width of the arrow bitmap
    Private Const BitmapWidth As Byte = 25

    'max possible value of the scrollbar
    Private m_nMaxValue As Long = 100
    'min possible value of the scrollbar
    Private m_nMinValue As Long = 0
    'absolute value of the scrollbar
    Private m_nValue As Long = 0
    'value of the scrollbar
    Private m_nRealValue As Long = 0

    'left arrow image
    Private m_imgLeftImage As Bitmap
    'right arrow image
    Private m_imgRightImage As Bitmap

    'left value of Thumbleft
    Private m_fThumbLeft As Single = 26.0F
    'flag to indicate if mouse is hover the thumb
    Private m_bMouseOnThumb As Boolean = False  
    ' flag to indicate whether left mouse button pushed
    Private m_bMouseDown As Boolean = False
    
    'which button is clicked: Left-True; Right-False
    Private m_bArrowClicked As Boolean      
       
    '''color properties
    'Border color
    Private m_cBorderColor As Color = Color.White
    'right track begin color
    Private m_cRightChannelBeginColor As Color = Color.Honeydew
    'right track end color
    Private m_cRightChannelEndColor As Color = Color.Gray
    'left track begin color
    Private m_cLeftChannelBeginColor As Color = Color.Green
    'left track end color
    Private m_cLeftChannelEndColor As Color = Color.White  
    'thumb's fill color(the left and right Half eclipse's color)
    Private m_cThumbFillColor As Color = Color.Blue
    'center color of the thumb
    Private m_cThumbRectColor As Color = Color.LightYellow  
    '''end color properties

    'layout, horizontal or vertical
    Private m_BarLayout As BarLayout = BarLayout.Horizontal

Step 1: draw arrows and track border

As it might be a little complicated to draw arrow buttons and the user might want to customize the arrow picture, I use bitmaps as arrow picture and directly draw them onto each side of the control. Initially, I restricted the control's height to BitmapHeight+2 pixels, the arrow picture size is BitmapWidth * BitmapHeight (pixels). You can modify these values later. If the user doesn't specify a different picture, it will use the default arrows, the same as those in Dietrich's. For the track border, I draw a rectangle along the entire side of the control.

Dim gTrack As Graphics = Me.CreateGraphics
'draw track border
gTrack.DrawRectangle(New Pen(m_cBorderColor), _
  0, 0, Me.Width - 1, Me.Height - 1)

'draw arrow to each side of the track
gTrack.DrawImage(m_imgLeftImage, 1, 1)
gTrack.DrawImage(m_imgRightImage, Me.Width - BitmapWidth - 1, 1)

Step 2: draw channels

For both the channels, I use the LinearGradientBrush to fill in the area, which gives the entire control a 3D look. The user can customize the color of each channel through corresponding properties, which I will describe later. The left channel and right channel brush are defined like this:

Dim rightBrush As New Drawing2D.LinearGradientBrush(ClientRectangle,_ 
        m_cRightChannelBeginColor, m_cRightChannelEndColor, _
        Drawing2D.LinearGradientMode.Vertical)
Dim leftBrush As New Drawing2D.LinearGradientBrush(ClientRectangle, _
        m_cLeftChannelBeginColor,_ m_cLeftChannelEndColor, _
        Drawing2D.LinearGradientMode.BackwardDiagonal)

There are two points that must be kept in mind when drawing the channels:

  1. you shouldn't fill the track border;
  2. both channels exclude the arrows, so the arrow area should not be overlaid.

Consequently, the rectangle area of each channel is defined as below:

Dim fTmpRightChannelWidth As Single
Dim LeftChannel As Rectangle = _
   New Rectangle(BitmapWidth + 1, 2, 0, BitmapHeight - 2), _
   RightChannel As RectangleF

LeftChannel.Width = CalValue() - BitmapWidth + BitmapHeight / 2
If LeftChannel.Width <= 0 Then
  LeftChannel.Width = BitmapHeight / 2
  fTmpRightChannelWidth = Me.Width - BitmapWidth * 2 - 2
  RightChannel = New RectangleF(BitmapWidth + 1, 1, _
    fTmpRightChannelWidth, BitmapHeight)
'The function CalValue() returns the current
' thumb's left edge's x-coordinate.
Private Function CalValue() As Single
    Return BitmapWidth + 1 + (Me.Width - BitmapWidth * 3 - 2) _
       * m_nValue / (m_nMaxValue - m_nMinValue)
End Function

Next, fill in the corresponding area using the brush defined above:

gTrack.FillRectangle(rightBrush, RightChannel)
gTrack.FillRectangle(leftBrush, LeftChannel)
gTrack.DrawRectangle(New Pen(Color.Gray), LeftChannel.X, LeftChannel.Y, _
  LeftChannel.Width, LeftChannel.Height - 1)

Step 3: Draw thumb

Until very recently, I haven't figured out a good-looking style of the thumb, so the current result of the style of the thumb might look a bit ugly; however, the drawing process is almost the same.

The size of the thumb is the same as that of the arrow. Before drawing the thumb, you must first calculate the position of the thumb. This is done by the function CalValue(), as described earlier. The thumb area is composed of a pie area at each side and a rectangle area in the center. I use a GraphicsPath class to create such an area and use a PathGradientBrush to fill in the area.

        Dim gThumb As Graphics = Me.CreateGraphics
        'pen to draw the edge of the area
        Dim linePen As New Pen(Color.Gray, 1)
        Dim fX(3) As Single

        'initialize edge values--------------------------------------
        fX(0) = CalValue()
        If (m_BarLayout = BarLayout.Horizontal) Then
            fX(1) = fX(0) + BitmapHeight / 2
            fX(2) = fX(1) + (BitmapWidth - BitmapHeight) / 2 '7
        Else
            fX(1) = fX(0) + BitmapWidth / 2
            fX(2) = fX(1) + (BitmapHeight - BitmapWidth) / 2 '7
        End If
        '''----------------------------------------------------------

        'define path and brushes
        Dim rectPath As New Drawing2D.GraphicsPath
        'path that will constitute the thumb area
        If (m_BarLayout = BarLayout.Horizontal) Then
            Dim rect2Fill As New RectangleF(fX(1), 1.0F, _
                BitmapWidth - BitmapHeight, BitmapHeight)
                'center rectangle area of the thumb
            Dim rect2Fill As New RectangleF(fX(1), 1.0F, fX(2)_
               - fX(0), BitmapHeight)
               'center rectangle area of the thumb
            rectPath.AddArc(fX(0), 0.3F, BitmapHeight, _
                        BitmapHeight, 90, 180) 'left pie
            rectPath.AddRectangle(rect2Fill) 'center
            rectPath.AddArc(fX(2), 0.3F, BitmapHeight, _
                        BitmapHeight, 90, -180) 'right pie
        Else
            Dim rect2Fill As New RectangleF(1.0F, fX(2), _
                    BitmapWidth, 2 * fX(1) - fX(0) - fX(2))
            rectPath.AddArc(0.3F, 2 * fX(1) - fX(0), _
                    BitmapWidth, BitmapWidth, 0, 180) 'upper pie
            rectPath.AddRectangle(rect2Fill) 'center
            rectPath.AddArc(0.3F, fX(2) - fX(1) + fX(0), _
                    BitmapWidth, BitmapWidth, -180, 180) 'down pie
        End If
        Dim rectBrush As New Drawing2D.PathGradientBrush(rectPath)
        rectBrush.CenterColor = m_cThumbRectColor
        Dim rectColors As Color() = {m_cThumbFillColor, _
            m_cThumbFillColor, m_cThumbFillColor, m_cThumbFillColor}
        rectBrush.SurroundColors = rectColors

        'draw the thumb
        If (m_BarLayout = BarLayout.Horizontal) Then
            gThumb.DrawArc(linePen, fX(0), 1.0F, _
                   BitmapHeight, BitmapHeight - 1, 90, 180)
            gThumb.DrawArc(linePen, fX(2), 1.0F, _
                   BitmapHeight, BitmapHeight - 1, -90, 180)
        Else
            gThumb.DrawArc(linePen, 1.0F, 2 * fX(1) - fX(0), _
                   BitmapWidth, BitmapWidth - 1, 0, 180)
            gThumb.DrawArc(linePen, 1.0F, fX(2) - fX(1) + fX(0), _
                   BitmapWidth, BitmapWidth - 1, 0, -180)
        End If
        gThumb.FillPath(rectBrush, rectPath)

Step 4: Add them together

Just do the jobs described in the first three steps every time the value of the scrollbar changes and the bar will appear like the one described at the beginning of the article.

Turn our scrollbar into a real Cool Scrollbar

Add overrides

Up to now, our scrollbar has its “face”, but is nothing more than a picture. In order to be a real scrollbar, first it should be able to be scrolled. With the help of bunches of override subs and functions inherited from the System.Windows.Forms.UserControl class, it is easy to realize this.

As described in the Nomenclature section, the scrollbar has three areas: the channels, the thumb and the arrow buttons. Thus when the mouse is clicked or moved in these areas, there should be different events. For more details, please refer to my code, this section is written as clear as possible so that you can understand it well.

Add properties

As for the properties, I currently enable the following:

Behavior

  • Value: the current value of the scrollbar (Long)
  • MinValue: minimum value of the scrollbar (Long)
  • MaxValue: maximum value of the scrollbar (Long)

Appearance

  • LeftArrow: left arrow image (Bitmap)
  • RightArrow: right arrow image (Bitmap)
  • LeftChannelBeginColor: begin color of the left channel brush (Color)
  • LeftChannelEndColor: end color of the left channel brush (Color)
  • RightChannelBeginColor: begin color of the right channel brush (Color)
  • RightChannelEndColor: end color of the right channel brush (Color)
  • ThumbFillColor: center color of the thumb area (Color)
  • ThumbRectColor: surrounding color of the thumb area (Color)
  • TrackBorderColor: color of the border of the track (Color)
  • ScrollbarLayout: the layout of the scrollbar, vertical or horizontal.

The way of enabling these properties is the same as the ordinary one, just remember to redraw the track every time the property value changes.

This is what they look like in a Windows application property window:

You can modify them as you like.

Add event

Currently the Cool Scrollbar only needs one event to function correctly: ValueChanged. Just raise this event each time the MouseUp event occurs.

Future enhancements

  • A more stylish thumb
  • Flexible size property
  • Optional tooltip text indicating the value of the scrollbar
  • More...

Acknowledgement

Thanks to Hans Dietrich for the idea of creating the XScrollBar, as well as the clear explanation of the drawing process.

History

Version 0.1 - October 12, 2004

  • Initial public release.

Version 0.2 - April 25, 2005

  • Add vertical version of the scrollbar.
  • Update the thumb drawing method.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

superliu
Web Developer
United States United States
Member
Graduated from Wuhan University(EE), China in june 2004, I worked for Fiberhome Telecommunication Technologies, Co., LTD, WRI for one year, and now I am pursuing my graduate studies in Electrical Engineering. Basicly I wrote programs in my spare time.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralColored Scrollbarmemberoptimal-process.de7 Jun '10 - 8:08 
Your work looks so good, but we cannot bring it to work
... as some others in that area wrote: changes of the arrows and resizing from 14 to any other width or hight resp.
is impossible
... even the resource - editor you suggested is not working (bring conversion errors
 
Would it be poss. to install a res.-file in your project and read the bmp from there?
Would it be poss. to let the user change the property-size and dyn. change the arrow-bmp's ???
I would appriciate an answer, because we really would like to use this very nice thing
regards Thomas
GeneralMy vote of 1memberjuras921 Jun '09 - 6:47 
poor
Generalproblems with arrow imagesmemberTodor Todorov3 Jan '09 - 4:01 
Hello, I really need a help
Why I can`t visualize the left and right arrow images.
everything is like you posted in the source code but it can be seen only the scroll area , not the arrow`s area.
 
What did you mean with this :
 
"If you want to show an image, an Image object must be created and instantiated. For example:
 
Dim myBitmap as New Bitmap(System.Environment.GetFolderPath _
(System.Environment.SpecialFolder.MyPictures))
 
Then you can load this image to the Graphics object in a specific location:
 
g.DrawImage(myBitmap, 1, 1)
 
Don't forget to free memory after using the Graphics and Image objects:
 
g.Dispose()
myBitmap.Dispose() "
 
Thank you in advance
GeneralGreat ScrollBarmemberspammehard200224 Apr '07 - 7:18 
This is a great control!
GeneralChanging the left/right buttonsmemberWingman-SSJ28 Feb '06 - 23:22 
Hey, I'm trying to use your component which looks very neat! As I read in another message you can change the height of the scrollbar by using other LeftArrow and RightArrow images, as the scrollbar gets it's height from these images. But I cannot get this to work. I've tried to open a 50x50 bmp and 50x50 jpg. Both converted to a New System.Drawing.Bitmap by VB.Net. But your component doesn't use them. I also tried this using the propery window after adding a CBar to the ToolBox, but when I open thes images the property window pops back to the mainform. Using the Size property also doesn't work for me.
 
I want to use it vertically, but since I have to use it on a touch-screen it has to be wider than the standard size. I'd surely appreciate your help!
 
Thanks,
Wingman-SSJ
GeneralRe: Changing the left/right buttonsmembersuperliu13 Mar '06 - 14:53 
Hi there,
 
first sorry for the late reply I was very busy these months.
 
For your question, when I created the control I do not load the bitmap from the control image, but I load them from resource file which you can find in my package ("CBar2.resx"). To edit a resource file you will need this tool:
http://download.microsoft.com/download/7/f/0/7f035772-9add-4caa-ba54-979385904b40/ResourceEditor.exe[^]
 
Please feel free to contact me. Thanks for your interests!
 
Endeavor brings you freedom!
Questionwork on web app ??memberAroraG21 Oct '05 - 8:27 
Hi
 
Does your tool works for Web Applications? I have been trying to make it work for so long on my web app, but no success!!! Frown | :-(
 
Please help me.
 
Thanks
Gaurav Arora
AnswerRe: work on web app ??membersuperliu13 Mar '06 - 14:56 
First I am really sorry for the late reply;
 
What kind of web application are you working on? This control is created based on the .net framework 1.1, any web app based on .net framework 1.1 should work.
 
Thanks for your interests!
 
Endeavor brings you freedom!
QuestionA questionmemberwind886814 Oct '05 - 15:01 
Hello Superliu,
 
It seems it's hard to move the thumb very fast. Is there a way to make it happen? Thanks!
AnswerRe: A questionmembersuperliu15 Oct '05 - 10:15 
If what you mean is by clicking the arrow buttons to move the thumb, then just make the interval of the timer smaller because the movement of the thumb when you click on the arrow button is controled by the timer event.
 
Endeavor brings you freedom!
GeneralRe: A questionmemberwind886817 Oct '05 - 6:25 
Thanks for the answer. What I mean was how to move the thumb faster by pressing on the thumb (mouse down) and drag it to the right or left (mouse move)? It seems to me that if I drag the thumb slowly, it works fine. If I drag the thumb very fast, however, it usually moves a little bit and then stops. In other words, the thumb does not move smoothly and as expected if I drag it fast. Thanks again!
 

QuestionHow to Add / Embed Scrollbar in Textbox..?sussGG Pradeep babu12 Jul '05 - 1:22 
Smile | :) Hi dude,
 
You have done a very good job. Well done. I have a question that, how to apply / embed your control in Textbox, when multiline is set to true and Scroll bar is set to " Horizontal / vertical", instead of displaying default windows scroll bar . Hope you know that when autoscroll in Textbox set to TRUE, it will display windows default scroll bars. Instead i want to display custom scroll bar like yours... Can you help me? Waiting for your Positive Reply.
 
Regards,
GG Pradeep babu [gg_pradeep_babu@yahoo.com]
AnswerRe: How to Add / Embed Scrollbar in Textbox..?membersuperliu12 Jul '05 - 22:22 
Thank you!
 
You can create a owner-draw textbox yourself and implement the scrollbar using the approches in my article by trapping the "WM_HSCROLL" and "WM_VSCROLL" messages
 
Endeavor brings you freedom!
GeneralVC++memberefk10 May '05 - 6:04 
Hello, it looks really nice.
 
Is there any possibility to include it in a VC++ 6.0 project ?
 
Regards.
Bob

GeneralRe: VC++membersuperliu10 May '05 - 17:50 
Hello, Bob,
 
I am very glad that you like it; however, it is not possible to use it in a Visual C++ 6.0 project, because the code is written completely in .net language. If you would like to use it in C++, you can implement it in the managed C++, in Visual C++.net.
 
If you still want to use the control in VC6, you can refer to Hans Dietrich's "XScrollBar - Scroll bar like Windows Media Player's", http://www.codeproject.com/miscctrl/XScrollBar.asp[^]
 
Endeavor brings you freedom!
GeneralRe: VC++memberefk10 May '05 - 20:47 
Thank you for your quick answer.
I checked the XScrollBar. Unfortunately, it does not handle "PAGE_UP" and "PAGE_DOWN" message. The problem many people like me have is: we use the standard ScrollBar for many years, so we are very friendly with it and with all the Windows messages that come with.
 
Our dream is a NewScrollBar 100% compatible with the existing ScrollBar, just with the possibility to redraw it.
 
And believe me, it is very hard to find.
 
Best Regards.
Bob.

GeneralRe: VC++membersuperliu11 May '05 - 20:59 
It is true that a perfect stylish scrollbar is hard to find; however remember that we are here at codeproject, which provide us with more ideas and approaches which could spark our thoughts than merely code that we take away and use(at least as I see it).
 
In fact I have used the XScrollbar in one of my visual c++ project, I also found something not that good so I made some changes to Hans's code and is trying to implement some common scrollbar messages such as TB_BOTTOM, TB_PAGEDOWN, TB_PAGEUP and so on.
 
I think if we all do something for the project, the dream scrollbar will come one day.
 
Endeavor brings you freedom!
GeneralquestionsussT.O.K2 May '05 - 8:09 

can I use this as a user control.. ?
can I add the scrollbar to the toolbox and use it like that..
 
cause I keep having faults trying that..
 
thx
GeneralRe: questionmembersuperliu6 May '05 - 1:36 
of course you can!
 
create a .net project, open the toolbox, right click on the toolbox and choose "", on the Customize Toolbox dialog, choose the ".Net Framework Component", Click "Browse" button, choose the CoolScrollbar's dll "cbar.dll", click OK, then you will find the CBar control on the Windows Forms tab. place a bar on the form, like any other control.
 
if you still have questions, please provide the error information and send them to my email address.
 
Endeavor brings you freedom!
QuestionNew version?memberexpukpuk19 Mar '05 - 16:02 
Hi,
Are you working on second version 0.2 ?

AnswerRe: New version?membersuperliu22 Apr '05 - 19:59 
The new version will be posted within the next three days, the updated version will include the vertical version of the scrollbar
 
Endeavor brings you freedom!
Generalwidth changememberkgclacor11 Mar '05 - 1:25 
How can i change the width of the scrollbar?

GeneralRe: width changemembersuperliu13 Mar '05 - 21:09 
You can change the width of the scrollbar by setting the "Size" property;
 
The control's height is based on the height of the arrow image; consequently, if you want to change the height of the control, you should change the arrow image.
 
Endeavor brings you freedom!
GeneralThis is relly goodmemberexpukpuk21 Nov '04 - 13:30 
Thanks for you cool scrollbar! I am sure lots of people are waiting for a future version.
When cliking on the ends of the controll the value changes by 10 (when max = 100). Is there a way to make it jump by 3 for example?
 
deodatus
GeneralRe: This is relly goodmembersuperliu21 Nov '04 - 14:16 
the value of the scrollbar will change 1/10 points of the value range, which is 10 by default. This is controlled by a variable named nTmpValue in the OnMouseDown overrided sub(approximately in line 500 in my code), modify it as you like.
 
Endeavor brings you freedom!

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130523.1 | Last Updated 3 May 2005
Article Copyright 2004 by superliu
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid