Click here to Skip to main content
15,890,741 members
Articles / Programming Languages / C#
Article

Understand .NET Scrollbars

Rate me:
Please Sign up or sign in to vote.
4.57/5 (29 votes)
28 Dec 20043 min read 153.8K   3.3K   70   10
Thumb Size, Paging, and Sub-Paging issues.

Image 1

Introduction

Scrollbars seem easy at the surface, but I recently discovered that the .NET implementation needed a minor adjustment for my needs. I decided to write a little test application to study scrollbars and how the different properties affect the visual and functional aspects of a scrollbar.

How Does A ScrollBar Work?

There are three things about a scrollbar that you usually set:

  • The thumb, so that it reflects the visible portion of the document proportional to the entire document, which is represented by the entire track height (or width, for horizontal scrollbars).
  • How to set the scrollbar so that it correctly pages through a document when the user clicks on the track.
  • How to set the scrollbar so that you can correctly display sub-page changes when the user moves the thumb or uses the arrow buttons at the edges of the scrollbar.

This is easy enough to do in .NET with the following properties:

  • LargeChange sets the viewable "page" dimension.
  • Maximum sets the total document dimension.

Setting these two properties adjusts the scrollbar so that the thumb correctly reflects the viewable "page" proportional to the entire document breadth or width. The following equation illustrates the relationship between LargeChange and Maximum to the thumb length and scrollbar length (I'm using length here to reflect one dimension, width or height):

LargeChange     Thumb Length
-----------  =  ------------  
  Maximum       Track Length

Furthermore, when the user clicks on the track, the scrollbar's Value changes by the LargeChange value.

Sub-Page Scrolling

The assumption in the .NET implementation is that you will always want to scroll by a "page" (the LargeChange). I have a situation where I don't want to do this. I have an image viewer that has rows of images, and what I'd like is the LargeChange to scroll by rows, not the entire visible "page", which consists of multiple rows. At the same time, I'd like the SmallChange to scroll pixel rows so that the viewer scrolls smoothly. This second requirement means that I can't do this:

C#
LargeChange=1;       // 1 row
Maximum=numRows;     // total # of rows

because now I can't specify a sub-row SmallChange (this property is of type int). Instead, I should do something like this:

C#
SmallChange=1;                // 1 pixel of change
LargeChange=rowHeight;        // height of 1 row
Maximum=numRows * rowHeight   // total height of all rows

But now my thumb is no longer proportional to the visible "page"! The thumb now reflects the ratio of one row with regards to the whole document rather than the entire page to the whole document. While this works from a functional perspective, the visual aspect is wrong.

The Solution

The solution is to decouple the LargeChange (which has to be used to get the thumb set to the appropriate size) from the actual change in Value when the user clicks on the track. This is accomplished by overriding the WndProc method and trapping the page up/down messages in a specialized VScrollBar (or HScrollBar):

C#
protected override void WndProc(ref Message m)
{
  if (m.Msg == 8469)
  {
    switch((uint)m.WParam)
    {
      case 2: // page up
        if (this.Value - this.ValLargeChange > 0)
        {
          this.Value-=this.ValLargeChange;
        }
        else
        {
          this.Value=0;
        }
        break;

      case 3: // page down
        if (this.Value+this.LargeChange+this.ValLargeChange < this.Maximum)
        {
          this.Value+=this.ValLargeChange;
        }
        else
        {
          this.Value=this.Maximum - this.LargeChange;
        }
        break;

      default:
        base.WndProc (ref m);
        break;
    }
  }
  else
  {
    base.WndProc (ref m);
  }
}

This takes control of the default scrollbar behavior and changes the Value based on my desired large change (in the ValLargeChange property).

Conclusion

You can play with the demo to see the difference in operation. Note that this code isn't brilliant, the property names are clumsy, and the message values are hard coded. Obviously, it's not production code. It is, as the project name indicates, merely a study of the problem.

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


Written By
Architect Interacx
United States United States
Blog: https://marcclifton.wordpress.com/
Home Page: http://www.marcclifton.com
Research: http://www.higherorderprogramming.com/
GitHub: https://github.com/cliftonm

All my life I have been passionate about architecture / software design, as this is the cornerstone to a maintainable and extensible application. As such, I have enjoyed exploring some crazy ideas and discovering that they are not so crazy after all. I also love writing about my ideas and seeing the community response. As a consultant, I've enjoyed working in a wide range of industries such as aerospace, boatyard management, remote sensing, emergency services / data management, and casino operations. I've done a variety of pro-bono work non-profit organizations related to nature conservancy, drug recovery and women's health.

Comments and Discussions

 
QuestionThanks Pin
twocozz22-Oct-21 10:38
twocozz22-Oct-21 10:38 
PraiseGood work! Pin
Kitiara30616-Mar-20 16:45
Kitiara30616-Mar-20 16:45 
SuggestionSuggestion of my implementation Pin
wholsh5-Sep-11 5:52
wholsh5-Sep-11 5:52 
Thanks for your scrollbar implementation.

I made my implementation based on yours.
The points of it are:
1. Avoid to use additional properties such as 'ValLargeChange' and 'ValMaximum'.
2. Hold values of 'LargeChange' and 'Maximum' as user input.
3. Solve the case that SmallChange value is more than one.

The code as belows (Form1.vb):
---------------------------------
Namespace scrollBarStudy
Public Class Form1 : Inherits Form
#Region "Windows Form Designer generated code"
Private WithEvents sbSt As VScrollBar
Private WithEvents sbMc As MarcVScrollBar
Private WithEvents sbKm As KimVScrollBar
Private WithEvents nudSmall As NumericUpDown
Private WithEvents nudLarge As NumericUpDown
Private WithEvents nudMin As NumericUpDown
Private WithEvents nudMax As NumericUpDown

Private txbHeight As TextBox
Private txbValueSt As TextBox
Private txbValueMc As TextBox
Private txbValueKm As TextBox
Private tbxLargeSt As TextBox
Private tbxLargeMc As TextBox
Private tbxLargeKm As TextBox
Private tbxMaxSt As TextBox
Private tbxMaxMc As TextBox
Private tbxMaxKm As TextBox

Private label1 As Label
Private label2 As Label
Private label3 As Label
Private label4 As Label
Private label5 As Label
Private label6 As Label
Private label7 As Label

Private grbProperty As GroupBox

Private components As System.ComponentModel.Container = Nothing

Public Sub New()
InitializeComponent()
End Sub

Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing Then
If components IsNot Nothing Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub

Private Sub InitializeComponent()
Me.sbSt = New System.Windows.Forms.VScrollBar
Me.nudSmall = New System.Windows.Forms.NumericUpDown
Me.nudLarge = New System.Windows.Forms.NumericUpDown
Me.nudMin = New System.Windows.Forms.NumericUpDown
Me.nudMax = New System.Windows.Forms.NumericUpDown
Me.txbHeight = New System.Windows.Forms.TextBox
Me.txbValueSt = New System.Windows.Forms.TextBox
Me.txbValueMc = New System.Windows.Forms.TextBox
Me.txbValueKm = New System.Windows.Forms.TextBox
Me.tbxLargeSt = New System.Windows.Forms.TextBox
Me.tbxLargeMc = New System.Windows.Forms.TextBox
Me.tbxLargeKm = New System.Windows.Forms.TextBox
Me.tbxMaxSt = New System.Windows.Forms.TextBox
Me.tbxMaxMc = New System.Windows.Forms.TextBox
Me.tbxMaxKm = New System.Windows.Forms.TextBox
Me.label1 = New System.Windows.Forms.Label
Me.label2 = New System.Windows.Forms.Label
Me.label3 = New System.Windows.Forms.Label
Me.label4 = New System.Windows.Forms.Label
Me.label5 = New System.Windows.Forms.Label
Me.label6 = New System.Windows.Forms.Label
Me.label7 = New System.Windows.Forms.Label
Me.grbProperty = New System.Windows.Forms.GroupBox
Me.sbMc = New scrollBarStudy.MarcVScrollBar
Me.sbKm = New scrollBarStudy.KimVScrollBar
CType(Me.nudSmall, System.ComponentModel.ISupportInitialize).BeginInit()
CType(Me.nudLarge, System.ComponentModel.ISupportInitialize).BeginInit()
CType(Me.nudMin, System.ComponentModel.ISupportInitialize).BeginInit()
CType(Me.nudMax, System.ComponentModel.ISupportInitialize).BeginInit()
Me.grbProperty.SuspendLayout()
Me.SuspendLayout()
'
'sbSt
'
Me.sbSt.Anchor = CType(((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Bottom) _
Or System.Windows.Forms.AnchorStyles.Left), System.Windows.Forms.AnchorStyles)
Me.sbSt.LargeChange = 1
Me.sbSt.Location = New System.Drawing.Point(177, 100)
Me.sbSt.Maximum = 1
Me.sbSt.MinimumSize = New System.Drawing.Size(10, 0)
Me.sbSt.Name = "sbSt"
Me.sbSt.Size = New System.Drawing.Size(17, 240)
Me.sbSt.TabIndex = 1
'
'nudSmall
'
Me.nudSmall.Location = New System.Drawing.Point(101, 125)
Me.nudSmall.Maximum = New Decimal(New Integer() {1000, 0, 0, 0})
Me.nudSmall.Minimum = New Decimal(New Integer() {1, 0, 0, 0})
Me.nudSmall.Name = "nudSmall"
Me.nudSmall.Size = New System.Drawing.Size(45, 21)
Me.nudSmall.TabIndex = 4
Me.nudSmall.TextAlign = System.Windows.Forms.HorizontalAlignment.Center
Me.nudSmall.Value = New Decimal(New Integer() {1, 0, 0, 0})
'
'nudLarge
'
Me.nudLarge.Location = New System.Drawing.Point(101, 44)
Me.nudLarge.Maximum = New Decimal(New Integer() {1000, 0, 0, 0})
Me.nudLarge.Minimum = New Decimal(New Integer() {1, 0, 0, 0})
Me.nudLarge.Name = "nudLarge"
Me.nudLarge.Size = New System.Drawing.Size(45, 21)
Me.nudLarge.TabIndex = 5
Me.nudLarge.TextAlign = System.Windows.Forms.HorizontalAlignment.Center
Me.nudLarge.Value = New Decimal(New Integer() {1, 0, 0, 0})
'
'nudMin
'
Me.nudMin.Location = New System.Drawing.Point(101, 98)
Me.nudMin.Maximum = New Decimal(New Integer() {1000, 0, 0, 0})
Me.nudMin.Name = "nudMin"
Me.nudMin.Size = New System.Drawing.Size(45, 21)
Me.nudMin.TabIndex = 6
Me.nudMin.TextAlign = System.Windows.Forms.HorizontalAlignment.Center
'
'nudMax
'
Me.nudMax.Location = New System.Drawing.Point(101, 71)
Me.nudMax.Maximum = New Decimal(New Integer() {1000, 0, 0, 0})
Me.nudMax.Name = "nudMax"
Me.nudMax.Size = New System.Drawing.Size(45, 21)
Me.nudMax.TabIndex = 7
Me.nudMax.TextAlign = System.Windows.Forms.HorizontalAlignment.Center
'
'txbHeight
'
Me.txbHeight.Anchor = CType((System.Windows.Forms.AnchorStyles.Bottom Or System.Windows.Forms.AnchorStyles.Left), System.Windows.Forms.AnchorStyles)
Me.txbHeight.Location = New System.Drawing.Point(101, 319)
Me.txbHeight.Name = "txbHeight"
Me.txbHeight.ReadOnly = True
Me.txbHeight.Size = New System.Drawing.Size(45, 21)
Me.txbHeight.TabIndex = 8
Me.txbHeight.TabStop = False
Me.txbHeight.TextAlign = System.Windows.Forms.HorizontalAlignment.Center
'
'txbValueSt
'
Me.txbValueSt.Location = New System.Drawing.Point(163, 16)
Me.txbValueSt.Name = "txbValueSt"
Me.txbValueSt.ReadOnly = True
Me.txbValueSt.Size = New System.Drawing.Size(45, 21)
Me.txbValueSt.TabIndex = 9
Me.txbValueSt.TabStop = False
Me.txbValueSt.TextAlign = System.Windows.Forms.HorizontalAlignment.Center
'
'txbValueMc
'
Me.txbValueMc.Location = New System.Drawing.Point(214, 16)
Me.txbValueMc.Name = "txbValueMc"
Me.txbValueMc.ReadOnly = True
Me.txbValueMc.Size = New System.Drawing.Size(45, 21)
Me.txbValueMc.TabIndex = 10
Me.txbValueMc.TabStop = False
Me.txbValueMc.TextAlign = System.Windows.Forms.HorizontalAlignment.Center
'
'txbValueKm
'
Me.txbValueKm.Location = New System.Drawing.Point(265, 16)
Me.txbValueKm.Name = "txbValueKm"
Me.txbValueKm.ReadOnly = True
Me.txbValueKm.Size = New System.Drawing.Size(45, 21)
Me.txbValueKm.TabIndex = 11
Me.txbValueKm.TabStop = False
Me.txbValueKm.TextAlign = System.Windows.Forms.HorizontalAlignment.Center
'
'tbxLargeSt
'
Me.tbxLargeSt.Location = New System.Drawing.Point(163, 44)
Me.tbxLargeSt.Name = "tbxLargeSt"
Me.tbxLargeSt.ReadOnly = True
Me.tbxLargeSt.Size = New System.Drawing.Size(45, 21)
Me.tbxLargeSt.TabIndex = 13
Me.tbxLargeSt.TabStop = False
Me.tbxLargeSt.TextAlign = System.Windows.Forms.HorizontalAlignment.Center
'
'tbxLargeMc
'
Me.tbxLargeMc.Location = New System.Drawing.Point(214, 44)
Me.tbxLargeMc.Name = "tbxLargeMc"
Me.tbxLargeMc.ReadOnly = True
Me.tbxLargeMc.Size = New System.Drawing.Size(45, 21)
Me.tbxLargeMc.TabIndex = 14
Me.tbxLargeMc.TabStop = False
Me.tbxLargeMc.TextAlign = System.Windows.Forms.HorizontalAlignment.Center
'
'tbxLargeKm
'
Me.tbxLargeKm.Location = New System.Drawing.Point(265, 44)
Me.tbxLargeKm.Name = "tbxLargeKm"
Me.tbxLargeKm.ReadOnly = True
Me.tbxLargeKm.Size = New System.Drawing.Size(45, 21)
Me.tbxLargeKm.TabIndex = 15
Me.tbxLargeKm.TabStop = False
Me.tbxLargeKm.TextAlign = System.Windows.Forms.HorizontalAlignment.Center
'
'tbxMaxSt
'
Me.tbxMaxSt.Location = New System.Drawing.Point(163, 71)
Me.tbxMaxSt.Name = "tbxMaxSt"
Me.tbxMaxSt.ReadOnly = True
Me.tbxMaxSt.Size = New System.Drawing.Size(45, 21)
Me.tbxMaxSt.TabIndex = 16
Me.tbxMaxSt.TabStop = False
Me.tbxMaxSt.TextAlign = System.Windows.Forms.HorizontalAlignment.Center
'
'tbxMaxMc
'
Me.tbxMaxMc.Location = New System.Drawing.Point(214, 71)
Me.tbxMaxMc.Name = "tbxMaxMc"
Me.tbxMaxMc.ReadOnly = True
Me.tbxMaxMc.Size = New System.Drawing.Size(45, 21)
Me.tbxMaxMc.TabIndex = 17
Me.tbxMaxMc.TabStop = False
Me.tbxMaxMc.TextAlign = System.Windows.Forms.HorizontalAlignment.Center
'
'tbxMaxKm
'
Me.tbxMaxKm.Location = New System.Drawing.Point(265, 71)
Me.tbxMaxKm.Name = "tbxMaxKm"
Me.tbxMaxKm.ReadOnly = True
Me.tbxMaxKm.Size = New System.Drawing.Size(45, 21)
Me.tbxMaxKm.TabIndex = 18
Me.tbxMaxKm.TabStop = False
Me.tbxMaxKm.TextAlign = System.Windows.Forms.HorizontalAlignment.Center
'
'label1
'
Me.label1.AutoSize = True
Me.label1.ForeColor = System.Drawing.Color.DarkRed
Me.label1.Location = New System.Drawing.Point(174, -4)
Me.label1.Name = "label1"
Me.label1.Padding = New System.Windows.Forms.Padding(0, 5, 0, 0)
Me.label1.Size = New System.Drawing.Size(132, 17)
Me.label1.TabIndex = 19
Me.label1.Text = "Std. Marc's Kim's"
Me.label1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft
'
'label2
'
Me.label2.AutoSize = True
Me.label2.Location = New System.Drawing.Point(6, 17)
Me.label2.Name = "label2"
Me.label2.Padding = New System.Windows.Forms.Padding(0, 5, 0, 0)
Me.label2.Size = New System.Drawing.Size(49, 17)
Me.label2.TabIndex = 20
Me.label2.Text = "Value : "
Me.label2.TextAlign = System.Drawing.ContentAlignment.MiddleLeft
'
'label3
'
Me.label3.AutoSize = True
Me.label3.Location = New System.Drawing.Point(6, 44)
Me.label3.Name = "label3"
Me.label3.Padding = New System.Windows.Forms.Padding(0, 5, 0, 0)
Me.label3.Size = New System.Drawing.Size(89, 17)
Me.label3.TabIndex = 21
Me.label3.Text = "Large Change:"
Me.label3.TextAlign = System.Drawing.ContentAlignment.MiddleLeft
'
'label4
'
Me.label4.AutoSize = True
Me.label4.Location = New System.Drawing.Point(6, 71)
Me.label4.Name = "label4"
Me.label4.Padding = New System.Windows.Forms.Padding(0, 5, 0, 0)
Me.label4.Size = New System.Drawing.Size(66, 17)
Me.label4.TabIndex = 22
Me.label4.Text = "Maximum:"
Me.label4.TextAlign = System.Drawing.ContentAlignment.MiddleLeft
'
'label5
'
Me.label5.AutoSize = True
Me.label5.Location = New System.Drawing.Point(6, 98)
Me.label5.Name = "label5"
Me.label5.Padding = New System.Windows.Forms.Padding(0, 5, 0, 0)
Me.label5.Size = New System.Drawing.Size(58, 17)
Me.label5.TabIndex = 23
Me.label5.Text = "Minimum"
Me.label5.TextAlign = System.Drawing.ContentAlignment.MiddleLeft
'
'label6
'
Me.label6.AutoSize = True
Me.label6.Location = New System.Drawing.Point(6, 125)
Me.label6.Name = "label6"
Me.label6.Padding = New System.Windows.Forms.Padding(0, 5, 0, 0)
Me.label6.Size = New System.Drawing.Size(89, 17)
Me.label6.TabIndex = 24
Me.label6.Text = "Small Change:"
Me.label6.TextAlign = System.Drawing.ContentAlignment.MiddleLeft
'
'label7
'
Me.label7.Anchor = CType((System.Windows.Forms.AnchorStyles.Bottom Or System.Windows.Forms.AnchorStyles.Left), System.Windows.Forms.AnchorStyles)
Me.label7.AutoSize = True
Me.label7.Location = New System.Drawing.Point(6, 319)
Me.label7.Name = "label7"
Me.label7.Padding = New System.Windows.Forms.Padding(0, 5, 0, 0)
Me.label7.Size = New System.Drawing.Size(44, 17)
Me.label7.TabIndex = 25
Me.label7.Text = "Height:"
Me.label7.TextAlign = System.Drawing.ContentAlignment.MiddleLeft
'
'grbProperty
'
Me.grbProperty.Anchor = CType(((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Bottom) _
Or System.Windows.Forms.AnchorStyles.Left), System.Windows.Forms.AnchorStyles)
Me.grbProperty.Controls.Add(Me.sbSt)
Me.grbProperty.Controls.Add(Me.sbMc)
Me.grbProperty.Controls.Add(Me.sbKm)
Me.grbProperty.Controls.Add(Me.txbHeight)
Me.grbProperty.Controls.Add(Me.txbValueSt)
Me.grbProperty.Controls.Add(Me.txbValueMc)
Me.grbProperty.Controls.Add(Me.txbValueKm)
Me.grbProperty.Controls.Add(Me.tbxLargeSt)
Me.grbProperty.Controls.Add(Me.tbxLargeMc)
Me.grbProperty.Controls.Add(Me.tbxLargeKm)
Me.grbProperty.Controls.Add(Me.tbxMaxSt)
Me.grbProperty.Controls.Add(Me.tbxMaxMc)
Me.grbProperty.Controls.Add(Me.tbxMaxKm)
Me.grbProperty.Controls.Add(Me.nudSmall)
Me.grbProperty.Controls.Add(Me.nudLarge)
Me.grbProperty.Controls.Add(Me.nudMin)
Me.grbProperty.Controls.Add(Me.nudMax)
Me.grbProperty.Controls.Add(Me.label1)
Me.grbProperty.Controls.Add(Me.label2)
Me.grbProperty.Controls.Add(Me.label3)
Me.grbProperty.Controls.Add(Me.label4)
Me.grbProperty.Controls.Add(Me.label5)
Me.grbProperty.Controls.Add(Me.label6)
Me.grbProperty.Controls.Add(Me.label7)
Me.grbProperty.Location = New System.Drawing.Point(6, 4)
Me.grbProperty.Name = "grbProperty"
Me.grbProperty.Size = New System.Drawing.Size(321, 349)
Me.grbProperty.TabIndex = 0
Me.grbProperty.TabStop = False
Me.grbProperty.Text = "Properties"
'
'sbMc
'
Me.sbMc.Anchor = CType(((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Bottom) _
Or System.Windows.Forms.AnchorStyles.Left), System.Windows.Forms.AnchorStyles)
Me.sbMc.LargeChange = 1
Me.sbMc.Location = New System.Drawing.Point(228, 100)
Me.sbMc.Maximum = 0
Me.sbMc.MinimumSize = New System.Drawing.Size(10, 0)
Me.sbMc.Name = "sbMc"
Me.sbMc.Size = New System.Drawing.Size(17, 240)
Me.sbMc.TabIndex = 2
Me.sbMc.ValLargeChange = 1
Me.sbMc.ValMaximum = 0
Me.sbMc.ValSmallChange = 1
Me.sbMc.Viewable = 1
'
'sbKm
'
Me.sbKm.Anchor = CType(((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Bottom) _
Or System.Windows.Forms.AnchorStyles.Left), System.Windows.Forms.AnchorStyles)
Me.sbKm.LargeChange = 1
Me.sbKm.Location = New System.Drawing.Point(279, 100)
Me.sbKm.Maximum = 0
Me.sbKm.MinimumSize = New System.Drawing.Size(10, 0)
Me.sbKm.Name = "sbKm"
Me.sbKm.Size = New System.Drawing.Size(17, 240)
Me.sbKm.SmallChange = 0
Me.sbKm.TabIndex = 3
'
'Form1
'
Me.AutoScaleBaseSize = New System.Drawing.Size(6, 14)
Me.ClientSize = New System.Drawing.Size(334, 359)
Me.Controls.Add(Me.grbProperty)
Me.MinimumSize = New System.Drawing.Size(342, 228)
Me.Name = "Form1"
Me.Text = "ScrollBar Study"
CType(Me.nudSmall, System.ComponentModel.ISupportInitialize).EndInit()
CType(Me.nudLarge, System.ComponentModel.ISupportInitialize).EndInit()
CType(Me.nudMin, System.ComponentModel.ISupportInitialize).EndInit()
CType(Me.nudMax, System.ComponentModel.ISupportInitialize).EndInit()
Me.grbProperty.ResumeLayout(False)
Me.grbProperty.PerformLayout()
Me.ResumeLayout(False)
End Sub
#End Region

''' The main entry point for the application.
<stathread()> _
Shared Sub Main()
Application.Run(New Form1())
End Sub

Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
sbSt.SmallChange = 1 : sbSt.LargeChange = 1 : sbSt.Minimum = 0 : sbSt.Maximum = 5 : sbSt.Value = 0
sbMc.SmallChange = 1 : sbMc.ValLargeChange = 1 : sbMc.Minimum = 0 : sbMc.ValMaximum = 5 : sbMc.Value = 0
sbKm.SmallChange = 1 : sbKm.LargeChange = 1 : sbKm.Minimum = 0 : sbKm.Maximum = 5 : sbKm.Value = 0

nudSmall.Value = sbSt.SmallChange : nudLarge.Value = sbSt.LargeChange
nudMin.Value = sbSt.Minimum : nudMax.Value = sbSt.Maximum
DispValue(True)

'AddHandler Me.Layout, AddressOf Me_Layout
Me_Resize(Me, EventArgs.Empty)
AddHandler Me.Resize, AddressOf Me_Resize

AddHandler Me.sbSt.ValueChanged, AddressOf Me.sBar_ValueChanged
AddHandler Me.sbMc.ValueChanged, AddressOf Me.sBar_ValueChanged
AddHandler Me.sbMc.Layout, AddressOf sbMc.sb2_Layout
AddHandler Me.sbKm.ValueChanged, AddressOf Me.sBar_ValueChanged

AddHandler Me.nudSmall.ValueChanged, AddressOf Me.nudSmall_ValueChanged
'AddHandler Me.nudSmall.Leave, AddressOf Me.nudSmall_ValueChanged

AddHandler Me.nudLarge.ValueChanged, AddressOf Me.nudLarge_ValueChanged
'AddHandler Me.nudLarge.Leave, AddressOf Me.nudLarge_ValueChanged

AddHandler Me.nudMin.ValueChanged, AddressOf Me.nudMin_ValueChanged
'AddHandler Me.nudMin.Leave, AddressOf Me.nudMin_ValueChanged

AddHandler Me.nudMax.ValueChanged, AddressOf Me.nudMax_ValueChanged
'AddHandler Me.nudMax.Leave, AddressOf Me.nudMax_ValueChanged
End Sub

'Private Sub Me_Layout(ByVal sender As Object, ByVal e As LayoutEventArgs)
Private Sub Me_Resize(ByVal sender As Object, ByVal e As EventArgs)
txbHeight.Text = sbSt.Height.ToString()
DispValue()
End Sub

#Region " Scrollbar "
Private _Recurse As Boolean = False
Private Sub sBar_ValueChanged(ByVal sender As Object, ByVal e As EventArgs)
If Not _Recurse Then 'prevent self-recursion
_Recurse = True
Dim v1 As Integer
Select Case CType(sender, ScrollBar).Name.ToString
Case "sbSt" : v1 = sbSt.Value
Case "sbMc" : v1 = sbMc.Value
Case "sbKm" : v1 = sbKm.Value
End Select
sbSt.Value = v1 : sbMc.Value = v1 : sbKm.Value = v1 'syncronize
DispValue()
_Recurse = False
End If
End Sub

Private Sub nudSmall_ValueChanged(ByVal sender As Object, ByVal e As EventArgs)
Dim v1 As Integer = Convert.ToInt32(nudSmall.Value)
If v1 <= nudLarge.Value Then
sbSt.SmallChange = v1
sbMc.SmallChange = v1
sbKm.SmallChange = v1
Else
nudSmall.Value = nudLarge.Value
End If
End Sub

Private Sub nudLarge_ValueChanged(ByVal sender As Object, ByVal e As EventArgs)
Dim v1 As Integer = Convert.ToInt32(nudLarge.Value)
sbSt.LargeChange = v1
sbMc.ValLargeChange = v1
sbKm.LargeChange = v1
If v1 < nudSmall.Value Then
nudSmall.Value = nudLarge.Value
End If
DispValue(True)
End Sub

Private Sub nudMin_ValueChanged(ByVal sender As Object, ByVal e As EventArgs)
Dim v1 As Integer = Convert.ToInt32(nudMin.Value)
If v1 <= nudMax.Value Then
sbSt.Minimum = v1
sbMc.Minimum = v1
sbKm.Minimum = v1
DispValue()
Else
nudMin.Value = nudMax.Value
End If
End Sub

Private Sub nudMax_ValueChanged(ByVal sender As Object, ByVal e As EventArgs)
Dim v1 As Integer = Convert.ToInt32(nudMax.Value)
If nudMin.Value <= v1 Then
sbSt.Maximum = v1
sbMc.ValMaximum = v1
sbKm.Maximum = v1
DispValue(True)
Else
nudMax.Value = nudMin.Value
End If
End Sub

Private Sub DispValue(Optional ByVal bLargeMax As Boolean = False)
If bLargeMax Then
tbxLargeSt.Text = sbSt.LargeChange.ToString
tbxLargeMc.Text = sbMc.LargeChange.ToString
tbxLargeKm.Text = sbKm.LargeChange.ToString
tbxMaxSt.Text = sbSt.Maximum.ToString
tbxMaxMc.Text = sbMc.Maximum.ToString
tbxMaxKm.Text = sbKm.Maximum.ToString
End If
txbValueSt.Text = sbSt.Value.ToString
txbValueMc.Text = sbMc.Value.ToString
txbValueKm.Text = sbKm.Value.ToString
End Sub
#End Region
End Class

''' Marc's scrollbar implementation.
Public Class MarcVScrollBar : Inherits VScrollBar
Protected _valSmallChange As Integer = 1
Public Property ValSmallChange() As Integer
Get
Return _valSmallChange
End Get
Set(ByVal value As Integer)
_valSmallChange = value
Me.SmallChange = _valSmallChange
End Set
End Property

Protected _valLargeChange As Integer = 1
Public Property ValLargeChange() As Integer
Get
Return _valLargeChange 'real value
End Get
Set(ByVal value As Integer)
_valLargeChange = value
End Set
End Property

Protected _Viewable As Integer = 1
Public Property Viewable() As Integer
Get
Return _Viewable
End Get
Set(ByVal value As Integer)
_Viewable = value
Recalc()
End Set
End Property

Protected _valMaximum As Integer = 1
Public Property ValMaximum() As Integer
Get
Return _valMaximum
End Get
Set(ByVal value As Integer)
_valMaximum = value
Me.LargeChange = Me.Height
Recalc()
End Set
End Property

'Public form As Form1
'Public Sub sb2_ValueChanged(ByVal sender As Object, ByVal e As System.EventArgs)
' form.tbValue2.Text = Me.Value.ToString()
'End Sub

Public Sub sb2_Layout(ByVal sender As Object, ByVal e As LayoutEventArgs)
' always set LargeChange to the height of the control.
Me.LargeChange = Me.Height
Recalc()
End Sub

Protected Sub Recalc()
Me.Maximum = ValMaximum
Me.LargeChange = Viewable
End Sub

Protected Overrides Sub WndProc(ByRef m As Message)
If m.Msg = 8469 Then
Select Case CUInt(m.WParam)
Case 2 ' page up
If Me.Value - Me.ValLargeChange > 0 Then
Me.Value -= Me.ValLargeChange
Else
Me.Value = 0
End If

Case 3 ' page down
If Me.Value + Me.LargeChange + Me.ValLargeChange < Me.Maximum Then
Me.Value += Me.ValLargeChange
Else
Me.Value = Me.Maximum - Me.LargeChange
End If

Case Else
MyBase.WndProc(m)
End Select
Else
MyBase.WndProc(m)
End If
End Sub
End Class

''' ScrollBar implementation.
''' in system, Thumb length reflect ratio of one page size to whole visible page numbers, however
''' in implementation, Thumb length refrect whole visible page numbers only.
''' rather than
''' i.e) system: Thumb Length = Min(Track Length * LargeChange / (Maximum - Minimum + 1), Track Length)
''' implementation = Track Length / (Maximum - Minimum + 1)
'''

''' <remarks>Refer: http://www.codeproject.com/KB/miscctrl/understandingScrollbars.aspx
Public Class KimVScrollBar : Inherits VScrollBar
Private _SmallChange As Integer
''' Implementation of system's SmallChange to adjust thumb size.
Public Overloads Property SmallChange() As Integer
Get
Return _SmallChange
End Get
Set(ByVal value As Integer)
If value > Me.LargeChange Then
_SmallChange = Me.LargeChange 'adjust samll value
Else
_SmallChange = value
End If
CheckValue()
End Set
End Property

Private _LargeChange As Integer
''' Implementation of system's LargeChange to adjust thumb size.
Public Overloads Property LargeChange() As Integer
Get
Return _LargeChange 'return real value
End Get
Set(ByVal value As Integer)
If value < Me.SmallChange Then
Me.SmallChange = value 'adjust samll value
End If
_LargeChange = value 'store real value
MyBase.LargeChange = 1 'set false value
CheckValue()
End Set
End Property

''' Implementation of system's Maximum to adjust thumb size.
Public Overloads Property Maximum() As Integer
Get
Return MyBase.Maximum
End Get
Set(ByVal value As Integer)
MyBase.Maximum = value
CheckValue()
End Set
End Property

''' Check if scroll position is match to system. Set match if not.
Private Sub CheckValue()
If Me.Value + _LargeChange > Me.Maximum Then
Me.Value = Math.Max(Me.Maximum - _LargeChange, Me.Minimum)
End If
End Sub

'--------------------------------------------------------------'alt: same result as belows
'Protected Overrides Sub WndProc(ByRef m As Message)
' 'Const WM_HSCROLL = &H114
' 'Const WM_VSCROLL = &H115
' If m.Msg = m.Msg = (&H2000 Or &H115) Then 'if WM_VSCROLL
' Select Case CInt(m.WParam)
' Case ScrollEventType.LargeDecrement 'page up
' Me.Value = Math.Max(Me.Value - Me.LargeChange, Me.Minimum)
' Case ScrollEventType.LargeIncrement 'page down
' '--------------------------------------------------'alt: goto end irrespective of reminding amount
' 'Me.Value = Math.Min(Me.Value + Me.LargeChange, Me.Maximum)
' '--------------------------------------------------'
' Me.Value += If(Me.Value + Me.LargeChange <= Me.Maximum, Me.LargeChange, 0)
' Case ScrollEventType.SmallDecrement 'arrow up
' Me.Value = Math.Max(Me.Value - Me.SmallChange, Me.Minimum)
' Case ScrollEventType.SmallIncrement 'arrow down
' Me.Value += If(Me.Value + Me.SmallChange <= Me.Maximum, Me.SmallChange, 0)
' Case Else
' MyBase.WndProc(m)
' End Select
' Else
' MyBase.WndProc(m)
' End If
'End Sub
'--------------------------------------------------------------'
Protected Overrides Sub OnScroll(ByVal e As ScrollEventArgs)
Select Case e.Type
Case ScrollEventType.LargeDecrement 'page up
e.NewValue = Math.Max(e.OldValue - Me.LargeChange, Me.Minimum)
Case ScrollEventType.LargeIncrement 'page down
'-----------------------------------------------------'alt: goto end irrespective of reminding amount
'e.NewValue = Math.Min(e.OldValue + Me.LargeChange, Me.Maximum)
'-----------------------------------------------------'
e.NewValue = e.OldValue + If(e.OldValue + Me.LargeChange <= Me.Maximum, Me.LargeChange, 0)
Case ScrollEventType.SmallDecrement 'arrow up
e.NewValue = Math.Max(e.OldValue - Me.SmallChange, Me.Minimum)
Case ScrollEventType.SmallIncrement 'arrow down
e.NewValue = e.OldValue + If(e.OldValue + Me.SmallChange <= Me.Maximum, Me.SmallChange, 0)
Case Else
MyBase.OnScroll(e)
End Select
End Sub
End Class
End Namespace
---------------------------------

P.S: I would like to excuse that the code is made by vb.Net and some names of variable were changed for my convinience.
GeneralUseful article Pin
oakash8-Jan-10 18:29
oakash8-Jan-10 18:29 
QuestionMessage IDs Pin
metator30-May-06 14:23
metator30-May-06 14:23 
AnswerRe: Message IDs Pin
Marc Clifton30-May-06 15:27
mvaMarc Clifton30-May-06 15:27 
QuestionHow to use Scrollbars Pin
sapphireadmin28-Oct-05 5:59
sapphireadmin28-Oct-05 5:59 
GeneralNice, but... Pin
Member 2611885-Feb-05 10:11
Member 2611885-Feb-05 10:11 
GeneralSuggestions/Comments... Pin
Andrew Smith30-Dec-04 6:32
Andrew Smith30-Dec-04 6:32 
GeneralNice one!!! Pin
Rajesh Pillai28-Dec-04 23:05
Rajesh Pillai28-Dec-04 23:05 

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

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.