Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Core Custom Scrollbar Class

0.00/5 (No votes)
23 Jul 2004 1  
This is a fully drawn GDI+ custom scrollbar control class. All of its drawing methods are overrideable, allowing developers to paint it however they choose.

Sample Image - example1.gif

Sample screenshot

Introduction

This is a fully drawn GDI+ custom scrollbar control class. All of its drawing methods are overrideable allowing developers to paint it however they choose.

Tutorial

Well, let me start off by mentioning why I wrote this class. I wrote this class because I read somewhere that you could custom draw the Windows scrollbar in .NET. Well, from that article, I tried to capture the wndproc() messages for custom drawing. But as I soon found out after tweaking the code several times over, Windows paints the scrollbar all over the place in different methods, making it impossible to custom draw due to flickering. I was so mad because I had spent so much time trying to make it work. So, I decided to start all over anew. I made my own custom control all from scratch.

All though very time consuming, I learned a lot of things about the Windows scrollbar class and how it works, from the links below:

Scrollbars seem to be one of those things Microsoft doesn't like to share with their source code. The articles above stated information about basic knowledge of how to use the scrollbars. They were lacking in depth information about how the scrollbars are calculated. Not only did they forget to explain how they are calculated, but they did not mention how to get size and location information from the thumb, shaft, buttons, etc. This information is not public information because Microsoft doesn't want you fooling around with it.

In my tutorial below, I will explain everything you want to know about scrollbars and more. Besides that, you will learn how to make a fully custom drawn control with event mapping.

Part 1 - Drawing your Fake Controls

Let's jump right into it. To make a custom control, you start off by inheriting the System.Windows.Forms.UserControl class. This class holds all the basic properties for your control. It's kinda like a basic starter template control for all you newbies out there. :) I started off building my control making the fun stuff first, the graphic design of it all!

There are two ways to design a custom control. One way is by using other pre-built MS controls to make a Composite control. This is a very effective way to get the job done fast. Although it is very fast to create, it may be very slow on load time, and eats up a lot of resources. Besides that, there are many things to the engine that can go wrong considering you are using other controls. You also cannot fix errors in pre-built controls, and you are limited to their design and functionality.

The second way is design by custom draw. This type of design takes forever, hehe. Although, the benefits are outstanding. To custom draw in .NET, you must import the System.Drawing classes. These classes give you full drawing capabilities inside your control. Custom drawing is called GDI+ in .NET. You can draw everything from circles, rectangles, fonts, lines, etc.

For my scrollbar class, I decided to pick the second way of design by drawing everything. My first method Draw() starts off by defining my sizes and locations of where I want everything to be. Keep in mind that none of the drawings are really controls. You have to keep track of all of their information like height, width, top, left and name. To do this, the best way is to create a class that stores all of the information and makes an array out of it. Below is the simple way to make this type class:

Public Class ControlInfo

#Region "Private Variables"

Private P_X As Integer
Private P_Y As Integer
Private P_H As Integer
Private P_W As Integer
Private P_Name As String
Private P_X2 As Integer
Private P_Y2 As Integer

#End Region

Public Property X() As Integer
 Get
  Return P_X
 End Get
 Set(ByVal Value As Integer)
  P_X = Value
 End Set
End Property

Public Property Y() As Integer
 Get
  Return P_Y
 End Get
 Set(ByVal Value As Integer)
  P_Y = Value
 End Set
End Property

Public Property H() As Integer
 Get
  Return P_H
 End Get
 Set(ByVal Value As Integer)
  P_H = Value
 End Set
End Property

Public Property W() As Integer
 Get
  Return P_W
 End Get
 Set(ByVal Value As Integer)
  P_W = Value
 End Set
End Property

Public Property Name() As String
 Get
  Return P_Name
 End Get
 Set(ByVal Value As String)
  P_Name = Value
 End Set
End Property

Public Property X2() As Integer
 Get
  Return P_X2
 End Get
 Set(ByVal Value As Integer)
  P_X2 = Value
 End Set
End Property

Public Property Y2() As Integer
 Get
  Return P_Y2
 End Get
 Set(ByVal Value As Integer)
  P_Y2 = Value
 End Set
End Property

Public Sub New()

 P_X = 0
 P_Y = 0
 P_H = 0
 P_W = 0
 P_Name = ""
 P_X2 = 0
 P_Y2 = 0

End Sub
End Class

This class stores all the information for the fake controls. To declare an array of it, to use in our main scrollbar class, is very simple.

Private Info(0) As ControlInfo

As you can see, it's declared to the size of zero. This is because my Draw() method defines it with the proper size and values. Let's get into the actual draw method now.

Private Sub Draw()

 'Set value to nothing-----
 Me.Value = 0
 '-------------------------

 'Redim Control list-------
 ReDim Info(5)
 Info(0) = New ControlInfo
 Info(1) = New ControlInfo
 Info(2) = New ControlInfo
 Info(3) = New ControlInfo
 Info(4) = New ControlInfo
 Info(5) = New ControlInfo
 PageUp = New Timer
 PageDown = New Timer
 '-------------------------


 'Declare Variables--------
 Dim x, y, h, w As Integer
 PageUp.Enabled = False
 PageUp.Interval = 500
 PageDown.Enabled = False
 PageDown.Interval = 500
 '-------------------------

 'Main Control--------------------------
 x = 0 : y = 0 : h = 0 : w = 0
 Info(0).X = x : Info(0).Y = y : Info(0).H = h : _
  Info(0).W = w : Info(0).X2 = (x + w) : _ 
  Info(0).Y2 = (y + h) : Info(0).Name = "ALL"
 '--------------------------------------

 'Thumb Control-------------------------
 Dim Thumbht As Integer = Get_Thumb_Height()
 x = 0 : y = 17 : h = Thumbht : w = Me.Width
 Info(1).X = x : Info(1).Y = y : Info(1).H = h :  _
  Info(1).W = w : Info(1).X2 = (x + w) : _ 
  Info(1).Y2 = (y + h) : Info(1).Name = "Thumb"
 Draw_Thumb(x, y, w, h, ControlEvents.None)
 '--------------------------------------

 'Shaft Control Above-------------------
 x = 0 : y = 17 : h = 0 : w = Me.Width
 Info(2).X = x : Info(2).Y = y : Info(2).H = h : _
  Info(2).W = w : Info(2).X2 = (x + w) : _
  Info(2).Y2 = (y + h) : Info(2).Name = "Shaft Above"
 Draw_Shaft_Above(x, y, w, h, ControlEvents.None)
 '--------------------------------------

 'Shaft Control Below-------------------
 If Thumbht > 0 Then Thumbht += 1
  x = 0 : y = 17 + Thumbht :  _
   h = Me.Height - 34 - Thumbht : w = Me.Width
 Info(3).X = x : Info(3).Y = y : Info(3).H = h : _ 
  Info(3).W = w : Info(3).X2 = (x + w) : _ 
  Info(3).Y2 = (y + h) : Info(3).Name = "Shaft Below"
 Draw_Shaft_Below(x, y, w, h, ControlEvents.None)
 '--------------------------------------

 'Draw Arrow Down---------------------
 x = 0 : y = Me.Height - 17 : h = 16 : w = Me.Width
 Info(4).X = x : Info(4).Y = y : Info(4).H = h : _ 
  Info(4).W = w : Info(4).X2 = (x + w) : _ 
  Info(4).Y2 = (y + h) : Info(4).Name = "Arrow Down"
 Draw_Arrow_Down(x, y, w, h, ControlEvents.None)
 '------------------------------------

 'Draw Arrow Up-----------------------
 x = 0 : y = 0 : h = 16 : w = Me.Width
 Info(5).X = x : Info(5).Y = y : Info(5).H = h : _ 
  Info(5).W = w : Info(5).X2 = (x + w) :  _
  Info(5).Y2 = (y + h) : Info(5).Name = "Arrow Up"
 Draw_Arrow_Up(x, y, w, h, ControlEvents.None)
 '------------------------------------

End Sub

The method above is very simple. We first re-declare the array sizes. We then start making our fake controls.

  1. Main User Control
  2. Thumb Control
  3. Shaft Control Above Thumb
  4. Shaft Control Below Thumb
  5. Scroll Arrow Button Up
  6. Scroll Arrow Button Down

We then set all of our fake properties, storing them in our controlinfo class. Last but not least, each fake control has its own drawing method. All of these drawing methods are overrideable, allowing for easy custom draw. They also do not leave you in the dark, because they pass several parameters to you. These parameters consist of location, size, and event type. Let's take a look at what one of the drawing methods looks like. This drawing method is Draw_Arrow_Up(). It draws the scroll arrow button up.

Public Overridable Sub Draw_Arrow_Up(ByVal X As Integer, _
  ByVal Y As Integer, ByVal W As Integer, ByVal H As Integer, _
  ByVal EventOf As ControlEvents)

 'Get Control Graphics-----------------
 Dim g As Graphics = Me.CreateGraphics
 g.SmoothingMode = SmoothingMode.None
 '-------------------------------------

 Select Case EventOf

  Case ControlEvents.None

   'Draw Rectangle to start--------------------------------------------
   g.FillRectangle(New SolidBrush(Color.White), _
     New Rectangle(X, Y, W, H))
   g.DrawRectangle(New Pen(Color.Gray), New Rectangle(X, Y, W - 1, H))
   '-------------------------------------------------------------------

   'Draw Border--------------------------------------------------------
   g.DrawLine(New Pen(Color.LightBlue), 3, 2 + Y, W - 4, 2 + Y)
   g.DrawLine(New Pen(Color.LightBlue), 2, Y + 3, 2, H + Y - 3)
   g.DrawLine(New Pen(Color.LightBlue), 3, Y + H - 2, W - 4, Y + H - 2)
   g.DrawLine(New Pen(Color.LightBlue), W - 3, Y + 3, W - 3, H + Y - 3)
   '-------------------------------------------------------------------

   'Draw Arrow---------------------------------------------------------
   g.DrawLine(New Pen(Color.Black), (W \ 2) - 1, (H \ 2) - 2, _
     (W \ 2) + 1, (H \ 2) - 2)
   g.DrawLine(New Pen(Color.Black), (W \ 2) - 2, (H \ 2) - 1, _
     (W \ 2) + 2, (H \ 2) - 1)
   g.DrawLine(New Pen(Color.Black), (W \ 2) - 3, (H \ 2), _
     (W \ 2) - 1, (H \ 2))
   g.DrawLine(New Pen(Color.Black), (W \ 2) + 1, (H \ 2), _
     (W \ 2) + 3, (H \ 2))
   g.DrawLine(New Pen(Color.Black), (W \ 2) + 2, (H \ 2) + 1, _
     (W \ 2) + 4, (H \ 2) + 1)
   g.DrawLine(New Pen(Color.Black), (W \ 2) - 4, (H \ 2) + 1, _
     (W \ 2) - 2, (H \ 2) + 1)
   '-------------------------------------------------------------------

   'Reset Settings-----------------------
   Info(5).X = X : Info(5).Y = Y : Info(5).H = H : Info(5).W = W : _
     Info(5).X2 = (X + W) : Info(5).Y2 = (Y + H) : _
     Info(5).Name = "Arrow Up"
   '-------------------------------------

  Case ControlEvents.OnMouseDown

   'Draw Rectangle to start--------------------------------------------
   g.FillRectangle(New SolidBrush(Color.LightBlue), _
     New Rectangle(X, Y, W, H))
   g.DrawRectangle(New Pen(Color.Gray), New Rectangle(X, Y, W - 1, H))
   '-------------------------------------------------------------------

   'Draw Border--------------------------------------------------------
   g.DrawLine(New Pen(Color.Blue), 3, 2 + Y, W - 4, 2 + Y)
   g.DrawLine(New Pen(Color.Blue), 2, Y + 3, 2, H + Y - 3)
   g.DrawLine(New Pen(Color.Blue), 3, Y + H - 2, W - 4, Y + H - 2)
   g.DrawLine(New Pen(Color.Blue), W - 3, Y + 3, W - 3, H + Y - 3)
   '-------------------------------------------------------------------

   'Draw Arrow---------------------------------------------------------
   g.DrawLine(New Pen(Color.Black), (W \ 2) - 1, (H \ 2) - 2, _
     (W \ 2) + 1, (H \ 2) - 2)
   g.DrawLine(New Pen(Color.Black), (W \ 2) - 2, (H \ 2) - 1, _
     (W \ 2) + 2, (H \ 2) - 1)
   g.DrawLine(New Pen(Color.Black), (W \ 2) - 3, (H \ 2), _
     (W \ 2) - 1, (H \ 2))
   g.DrawLine(New Pen(Color.Black), (W \ 2) + 1, (H \ 2), _
     (W \ 2) + 3, (H \ 2))
   g.DrawLine(New Pen(Color.Black), (W \ 2) + 2, (H \ 2) + 1, _
     (W \ 2) + 4, (H \ 2) + 1)
   g.DrawLine(New Pen(Color.Black), (W \ 2) - 4, (H \ 2) + 1, _
     (W \ 2) - 2, (H \ 2) + 1)
   '-------------------------------------------------------------------

   'Reset Settings-----------------------
   Info(5).X = X : Info(5).Y = Y : Info(5).H = H : Info(5).W = W : _
     Info(5).X2 = (X + W) : Info(5).Y2 = (Y + H) : _
     Info(5).Name = "Arrow Up"
   '-------------------------------------

  Case ControlEvents.OnMouseMove

   'Draw Rectangle to start--------------------------------------------
   g.FillRectangle(New SolidBrush(Color.White), _
     New Rectangle(X, Y, W, H))
   g.DrawRectangle(New Pen(Color.Gray), New Rectangle(X, Y, W - 1, H))
   '-------------------------------------------------------------------

   'Draw Border--------------------------------------------------------
   g.DrawLine(New Pen(Color.Blue), 3, 2 + Y, W - 4, 2 + Y)
   g.DrawLine(New Pen(Color.Blue), 2, Y + 3, 2, H + Y - 3)
   g.DrawLine(New Pen(Color.Blue), 3, Y + H - 2, W - 4, Y + H - 2)
   g.DrawLine(New Pen(Color.Blue), W - 3, Y + 3, W - 3, H + Y - 3)
   '-------------------------------------------------------------------

   'Draw Arrow---------------------------------------------------------
   g.DrawLine(New Pen(Color.Black), (W \ 2) - 1, (H \ 2) - 2, _
     (W \ 2) + 1, (H \ 2) - 2)
   g.DrawLine(New Pen(Color.Black), (W \ 2) - 2, (H \ 2) - 1, _
     (W \ 2) + 2, (H \ 2) - 1)
   g.DrawLine(New Pen(Color.Black), (W \ 2) - 3, (H \ 2), _
     (W \ 2) - 1, (H \ 2))
   g.DrawLine(New Pen(Color.Black), (W \ 2) + 1, (H \ 2), _
     (W \ 2) + 3, (H \ 2))
   g.DrawLine(New Pen(Color.Black), (W \ 2) + 2, (H \ 2) + 1, _
     (W \ 2) + 4, (H \ 2) + 1)
   g.DrawLine(New Pen(Color.Black), (W \ 2) - 4, (H \ 2) + 1, _
     (W \ 2) - 2, (H \ 2) + 1)
   '-------------------------------------------------------------------

   'Reset Settings-----------------------
   Info(5).X = X : Info(5).Y = Y : Info(5).H = H : Info(5).W = W : _
     Info(5).X2 = (X + W) : Info(5).Y2 = (Y + H) : Info(5).Name = "Arrow Up"
   '-------------------------------------

 End Select

End Sub

For all you people not familiar with drawing with GDI+, I start off by creating a variable to hold my graphics that I am going to paint. I set its smoothingmode to none to get a crisp look. There are many other modes in that enumeration to choose from, like highquality, which will try to blend your graphics. Next, I use a Case statement to figure out which event has occurred, so I can paint my graphics to that event. I use some basic GDI+ drawing methods like FillRectangle which makes solid rectangles, DrawRectangle which makes outlines of rectangles, and DrawLine which makes lines from one point to another. I then finish off by setting all of the fake properties in my controlinfo class once again. You may ask why twice? Well, that's how we can use this method more than once, from more than one place. The rest of the fake control drawing methods are very similar. They are easy to override if you want to make scrollbars that look fancy, or like XP style, or whatever you choose, it's up to you.

Part 2 - Making Fake Events for Your Fake Controls

How to make fake events? Well, to start out, the inherited usercontrol class lets you override its main events. This will give you a starting point. Knowing what events occurred is half the battle, the hard part is figuring out where the event occurred in relation to the location of your fake control. Since you have stored your fake control properties in your controlinfo class, figuring out if the event happened over your fake control is easy. Below are two methods to show you how this is done:

Private Function CursorPOS() As Integer

 'Get Cursor Location-----------------
 Dim CursorLocation As Point = Me.PointToClient(Cursor.Position)
 '------------------------------------

 'Check to make sure control has something------------------
 If UBound(Info) = 0 Then Return 0 : Exit Function
 '----------------------------------------------------------

 Dim i As Integer = 0
 For i = 0 To UBound(Info)
  'Check to see if cursor is over area-------------------
  If CursorLocation.X >= Info(i).X And CursorLocation.X < _
    Info(i).X2 And CursorLocation.Y >= Info(i).Y And _
    CursorLocation.Y <= Info(i).Y2 Then
      Return i
    Exit Function
  End If
  '------------------------------------------------------
 Next

 'Return Nothing------
 Return 0
 '--------------------

End Function

Protected Overrides Sub OnMouseMove(ByVal e As _
   System.Windows.Forms.MouseEventArgs)

 'Check if thumb moving----------------------------
 If ThumbMoving = True Then ThumbMover() : Exit Sub
 '-------------------------------------------------

 'If Mouse Down = True Then exit this method-------
 If MouseDownNow = True Then Exit Sub
 '-------------------------------------------------

 'Locate which control cursor is located above---
 Dim CheckValue As Integer = CursorPOS()
 '-----------------------------------------------

 'Check to see if mouse is already over location dont redraw--------
 If CheckValue = CurrentMouseMove Then
  Exit Sub
 Else

  Select Case CurrentMouseMove

   Case 1 : Draw_Thumb(Info(1).X, Info(1).Y, Info(1).W, Info(1).H, _
                                   ControlEvents.None)
   Case 4 : Draw_Arrow_Down(Info(4).X, Info(4).Y, Info(4).W, _
                        Info(4).H, ControlEvents.None)
   Case 5 : Draw_Arrow_Up(Info(5).X, Info(5).Y, Info(5).W, _
                        Info(5).H, ControlEvents.None)

  End Select

  'Set Current Mouse Move---------
  CurrentMouseMove = CheckValue
  '-------------------------------

  Select Case CheckValue

   Case 1 : Draw_Thumb(Info(CheckValue).X, Info(CheckValue).Y, _
    Info(CheckValue).W, Info(CheckValue).H, ControlEvents.OnMouseMove)
   Case 4 : Draw_Arrow_Down(Info(CheckValue).X, Info(CheckValue).Y, _
    Info(CheckValue).W, Info(CheckValue).H, ControlEvents.OnMouseMove)
   Case 5 : Draw_Arrow_Up(Info(CheckValue).X, Info(CheckValue).Y, _
    Info(CheckValue).W, Info(CheckValue).H, ControlEvents.OnMouseMove)

  End Select

 End If
 '------------------------------------------------------------------

End Sub

The first method CursorPOS() checks to see what fake control the event occurred over. It then returns its array ID. With the ID, you can then draw the new control and set its new properties. The second method OnMouseMove() does this. It finds out if the mouse moved over the fake control, then calls its drawing method to repaint it, which can be a nice looking highlight effect for mouseover like below:

Sample screenshot

In this example, the thumb is highlighted from the mouseover.

Part 3 - Sizing Fake Scrollbar Controls

This section is the part that Microsoft doesn't want to give away their secrets. They are the calculating methods for sizing the thumb and scrolling range. To calculate the thumb size is very simple.

Private Function Get_Thumb_Height() As Integer

 If Me.Maximum = 0 Or LargeChange = 0 Then Return 0 : Exit Function
 'Make thumb height based on number of records--------
 Dim ThumbHt As Integer = (Me.Height - 35) / (Me.Maximum / Me.LargeChange)
 '----------------------------------------------------


 'Get the thumb bar height-------------
 Select Case ThumbHt
  Case Is < 10
   Return 10
  Case Else
   Return ThumbHt
 End Select
 '-------------------------------------

End Function

First, we check to make sure properties are set by user in designmode. Then we calculate the thumb height. The equation is below:

  • ThumbHeight = ShaftHeight / (Maximum / LargeChange)

Easy you say? Yes, seems easy enough. We then need to check to see how big the thumb is. If the thumb is smaller than 10 in height, we wouldn't be able to click on it, so we make sure it is a minimum height of 10. If it is greater than 10, we return it as is.

Below is an example of the control resizing:

Sample screenshot Sample screenshot

As you can see, depending upon the maximum property value, the thumb height changes. The next calculation is the thumb sliding up and down the shaft. LOL Only if "beavis and butthead were here". The method is ThumbMover().

Private Sub ThumbMover()

 'Get Cursor Location-----------------
 Dim e As Point = Me.PointToClient(Cursor.Position)
 '------------------------------------

 'Get Position relative to where mouse was---------
 Dim NewPOS As Integer = e.Y + Info(1).Y - MeterY
 '-------------------------------------------------

 If NewPOS <= 18 Then
  If Info(1).Y <> 17 Then
   Draw_Thumb(0, 17, Me.Width, Info(1).H, ControlEvents.OnMouseDown)
   Draw_Shaft_Above(0, 0, Me.Width, 0, ControlEvents.None)
   Draw_Shaft_Below(0, Info(1).H + 18, Me.Width, Me.Height - 18 - _
     17 - Info(1).H, ControlEvents.None)
   Me.Value = Me.Minimum
   RaiseEvent Scroll()
  End If
  Exit Sub
 End If

 If NewPOS >= Me.Height - Info(1).H - 19 Then
  If Info(1).Y <> Me.Height - Info(1).H - 18 Then
    Draw_Thumb(Info(1).X, Me.Height - Info(1).H - 18, Info(1).W, _
      Info(1).H, ControlEvents.OnMouseDown)
    Draw_Shaft_Above(0, 17, Me.Width, Info(1).Y - 17, _
      ControlEvents.None)
    Draw_Shaft_Below(0, 0, Me.Width, 0, ControlEvents.None)
    Me.Value = Me.Maximum
    RaiseEvent Scroll()
  End If
  Exit Sub
 End If

 'Drawing moving Thumb-----------------------------------
 Draw_Thumb(Info(1).X, NewPOS, Info(1).W, Info(1).H, _
   ControlEvents.OnMouseDown)
 Draw_Shaft_Above(0, 17, Me.Width, Info(1).Y - 17, _
   ControlEvents.None)
 Draw_Shaft_Below(0, NewPOS + Info(1).H + 1, Me.Width, _
   Me.Height - 18 - NewPOS - Info(1).H, ControlEvents.None)
 MeterY = e.Y
 '-------------------------------------------------------

 'Make New Value-----------------------------------------
 Dim ScrollingArea As Integer = Me.Height - 34 - Info(1).H
 Me.Value = (Me.Maximum / ScrollingArea) * ((Info(1).Y - 17) - 1)
 '-------------------------------------------------------

 'Scroll-------------
 RaiseEvent Scroll()
 '-------------------

End Sub

Well, let me break it down for you. First, we get the cursor position. Then we calculate the new position with this equation:

  • NewPosition = CurrentCursorPosition + ThumbTop - OldCursorPosition

This will give us a new position for a moveable thumb. We then check to make sure the thumb is within the shaft. If it is out of range, we correct it. If it is in range, we then start the moving process. OK, now to get in depth on the secrets of the Windows scrollbar. To keep the scrollbar from flickering, we do not paint under the thumb. The shaft is in two pieces. The top shaft and bottom shaft. We draw from the top shaft to the thumb top, then we draw the bottom thumb to the bottom shaft. This reduces the drawing flicker to almost nothing. The next equation calculates the new control value.

The equation is:

  • Value = (Maximum / FullShaftSize) * ThumbTop

Seems simple enough! Then, last but not least, we raise a scroll event.

Part 4 - Scrolling Thumb based On Arrow Button Events

OK, we start off this section describing how Microsoft makes its arrow buttons work. How it clicks once and moves the thumb one position down, but if you continue holding the arrow button, it speeds up thumb moving. To create this effect, I use a timer control. Of course, how can you live in a world without time?

Private Sub Move_ThumbDown()

 Dim NewPos As Integer
 If Me.Value < Me.Maximum Then
  If Me.Value + Me.SmallChange > Me.Maximum Then
    Me.Value = Me.Maximum
  Else
   Me.Value += Me.SmallChange
  End If

  If Me.Value = Me.Maximum Then
   NewPos = Info(4).Y - Info(1).H - 1

   Draw_Thumb(Info(1).X, NewPos, Info(1).W, Info(1).H, _
         ControlEvents.None)
   Draw_Shaft_Above(0, 17, Me.Width, Info(1).Y - 17, _
         ControlEvents.None)
   Draw_Shaft_Below(0, NewPos + Info(1).H + 1, Me.Width, _
     (Me.Height - 18) - (Info(1).Y + Info(1).H), ControlEvents.None)
   '--------------------------------------------------

  Else

   NewPos = ((Me.Value) / (Me.Maximum)) * _
     (Info(4).Y - Info(1).H - 17) + 17

   Draw_Thumb(Info(1).X, NewPos, Info(1).W, Info(1).H, _
     ControlEvents.None)
   Draw_Shaft_Above(0, 17, Me.Width, Info(1).Y - 17, _
     ControlEvents.None)
   Draw_Shaft_Below(0, NewPos + Info(1).H + 1, Me.Width, _
     (Me.Height - 18) - (Info(1).Y + Info(1).H), _
   ControlEvents.None)
   '--------------------------------------------------

  End If

  'Scroll-------------
  RaiseEvent Scroll()
  '-------------------

 End If

End Sub

Private Sub PageDown_Tick(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles PageDown.Tick

If ShaftMovingDown = True Then

'Start Moving from Shaft-------
Move_ShaftDown()
'------------------------------

Else

'Start Moving from Thumb------
Move_ThumbDown()
'-----------------------------

End If

'Increase Timer Speed-------
PageDown.Interval = 50
'---------------------------

End Sub

The PageDown timer property (enabled) is set to True. It then calls the Move_ThumbDown() method. This creates a one click move for the thumb. The timer then increases its speed which, if the mouse is still down, increases the speed of the thumb movement.

Sample screenshot

Part 5 - Scrolling Thumb based on Shaft MouseDown Events

You have just seen in the previous section how to move the thumb by arrow buttons. Now, I am going to teach you how to move thumb with largechange, by clicking on the shaft. The following method creates this in combination with the previous timer method:

Private Sub Move_ShaftDown()

 Dim NewPos As Integer
 If Me.Value < Me.Maximum Then
  If Me.Value + Me.LargeChange > Me.Maximum Then
   Me.Value = Me.Maximum
  Else
   Me.Value += Me.LargeChange
  End If

  If Me.Value = Me.Maximum Then

   NewPos = Info(4).Y - Info(1).H - 1

   Draw_Thumb(Info(1).X, NewPos, Info(1).W, Info(1).H, _
     ControlEvents.OnMouseDown)
   Draw_Shaft_Above(0, 17, Me.Width, Info(1).Y - 17, _
     ControlEvents.None)
   Draw_Shaft_Below(0, NewPos + Info(1).H + 1, Me.Width, _
     (Me.Height - 18) - (Info(1).Y + Info(1).H), _
   ControlEvents.OnMouseDown)
   '--------------------------------------------------

  Else

   NewPos = ((Me.Value) / (Me.Maximum)) * _
      (Info(4).Y - Info(1).H - 17) + 17

   Draw_Thumb(Info(1).X, NewPos, Info(1).W, Info(1).H, _
      ControlEvents.OnMouseDown)
   Draw_Shaft_Above(0, 17, Me.Width, Info(1).Y - 17, _
      ControlEvents.None)
   Draw_Shaft_Below(0, NewPos + Info(1).H + 1, Me.Width, _
      (Me.Height - 18) - (Info(1).Y + Info(1).H), _
   ControlEvents.OnMouseDown)
   '--------------------------------------------------

  End If

  RaiseEvent Scroll()

 End If

End Sub

The code above starts out by checking to see if the value is below the maximum. If it is, it then highlights the shaft and moves the thumb. It only highlights the shaft half that was clicked on.

The below example shows this:

Sample screenshot

Part 6 - Scrolling Based on Key and Wheel Events

The methods below show how to capture the arrow and page keyboard keys, as well as the mouse wheel. To capture the keyboard keys, I use the ProcessDialogKey() method. To capture the mouse wheel, I use the onMouseWheel() method.

Protected Overrides Function ProcessDialogKey(ByVal keyData _
  As System.Windows.Forms.Keys) As Boolean


 Try
  Select Case keyData
   Case Keys.Up, Keys.PageUp
     If PageUp.Enabled = False Then
      Draw_Arrow_Up(Info(5).X, Info(5).Y, Info(5).W, Info(5).H, _
             ControlEvents.OnMouseDown)
      Move_ThumbUp()
      PageUp.Enabled = True
     End If
   Case Keys.Down, Keys.PageDown
    If PageDown.Enabled = False Then
      Draw_Arrow_Down(Info(4).X, Info(4).Y, Info(4).W, Info(4).H, _
        ControlEvents.OnMouseDown)
      Move_ThumbDown()
      PageDown.Enabled = True
    End If
  End Select
  Return True
 Catch ex As Exception
  Return False
 End Try

End Function

Protected Overrides Sub OnMouseWheel(ByVal e As _
             System.Windows.Forms.MouseEventArgs)

 If e.Delta > 0 Then
  Move_ThumbUp()
 Else
  Move_ThumbDown()
 End If

End Sub

Conclusion

Well, this concludes my tutorial on scrollbars. I hope you all enjoyed how long it was. ;) If anyone needs any help with understanding this article, don't forget to write.

Also one more thing, I am in the process of making the orientation of the scrollbar. So, currently you can only vertical scroll. Sorry! I am also making custom drawing presets for XP style, 3D style, flat style, and futuristic style.

"Devon Developers!" VectorX.

License

Free to use and modify as long as header stays in place. If any modifications are made to this assembly, users must email changes to author's email address.

Terms and Conditions For Use, Copy, Distribution and Modification:

THIS CODE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS CODE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here