Scrollbars are everywhere in graphical user interfaces (GUIs). They can serve two different purposes: to shift the area of a graphical surface that is visible to the user (by far the most common usage) or to vary a parameter (similar to a TrackBar, described in the next section).
The ScrollableControl class, and all classes derived from it, can implement scrollbars for the purpose of moving around in a window or a region of a window by setting a single property:
AutoScroll = true
Controls that derive, directly or indirectly, from ScrollableControl include Form and Panel, so for many applications, this is all you need to know about scrollbars. With the AutoScroll property set to true, the form or panel will automatically include either horizontal and/or vertical scrollbars, as necessary, if the extent of controls drawn on the form or panel exceeds the visible client area. This is true at runtime either as a result of program action or user interaction.
In addition, most other controls that need scrollbars, such as the multiline TextBox, listbox, or combo box, already have them provided as part of the base functionality of those controls. As with the ScrollableControl objects, scrollbars appear as necessary with no effort required on the part of the developer.
However, automatically created scrollbars are mostly inaccessible to the developer. (There is some control of AutoScroll scrollbars through the use of the AutoScrollMargin, AutoScrollMinSize, and AutoScrollPosition properties, but no events are exposed.) If you want to control the appearance or location of the scrollbars, apply them to a control that does not normally have them, or interact with them programmatically, then you must use the ScrollBar control.
The ScrollBar control is an abstract/MustInherit class derived directly from Control, as shown in Figure 13-1. Two classes are derived from it: HScrollBar for a horizontal scrollbar and VScrollBar for a vertical scrollbar.
With the ScrollBar control, you can
The ScrollBar class inherits all the Control properties, including the size and positioning properties such as Size, Location, Length, and Width. Height and Width have intuitively opposite meanings when you consider the HScrollBar versus the VScrollBar. In either case, Height refers to the vertical distance and Width refers to the horizontal distance. Thus the Height of a horizontal scrollbar is its thickness (its short axis), while the Height of a vertical scrollbar is its length (its long axis).
The properties that are specific to the ScrollBar control and provide much of its functionality are listed in Table 13-11.
Property |
Value type |
Description |
---|---|---|
LargeChange |
Integer |
Read/write. The value to be added or subtracted from the Value property when the user clicks in the scrollbar track between the thumb and the scroll arrow, or presses the Page Up or Page Down key. Default value is 10. In proportion to the Maximum property, determines the width of thumb. |
Maximum |
Integer |
Read/write. Maximum possible value of the Value property. Default value is 100. |
Minimum |
Integer |
Read/write. Minimum possible value of the Value property. Default value is zero. |
SmallChange |
Integer |
Read/write. The value to be added or subtracted from the Value property when the user clicks on the scroll arrow or presses the Up or Down arrow key. Default value is 1. |
Value |
Integer |
Read/write. Numeric value representing the current position of the thumb in the scrollbar. The current position is indicated by the left edge of the thumb of a horizontal scrollbar or the top edge of the thumb of a vertical scrollbar. The smallest value of Value is the Minimum property. The largest value of Value is (Maximum - LargeChange + 1). Default value is zero. |
Some of the standard Control properties are "missing" from the ScrollBar classi.e., they are overridden and not available at design time. You can code against them, but they are ignored. These properties include BackColor, BackgroundImage, ForeColor, and Text. In addition, the TabStop property is false, by default, unlike most controls, implying that scrollbars do not participate in the tab order.
There are two events raised by ScrollBar controls, listed in Table 13-12. The ValueChanged event is the most commonly used one: it is raised when the Value property is changed. At that point, the Value property reports the new value (after the change has occurred). If more granular information or higher performance is required, then the Scroll event can be trapped and handled.
Event |
Event data |
Description |
---|---|---|
ValueChanged |
EventArgs |
Raised when the Value property has changed, either by a Scroll event or programmatically. |
Scroll |
ScrollEventArgs |
Raised when the thumb has been moved, either by the mouse or the keyboard. Provides information about the event, including the new value and type of event. |
13.4.1 As a Positioning Device
The programs listed in Example 13-7 (in C#) and Example 13-8 (in VB.NET) demonstrate the use of horizontal and vertical scrollbars to scroll a large image within a smaller panel. Since the image is too large to be visible all at once, the scrollbars allow the user to pan around the image, viewing a portion of the image through the "viewport" provided by the panel.
In these examples, the image used is the screenshot from Figure 13-5, which is 990 pixels wide by 380 pixels high. You can use any image you want, as long as it is larger than the panel size of 400 x 200 (unless you change the size of the panel control).
When the programs are compiled and run, the result is shown in Figure 13-11, after adjusting the scrollbars to show the top middle of the image.
An analysis follows the program listings.
Figure 13-12. Scrollbars
Example 13-7. ScrollBars using C# (ScrollBars.cs)
using System; using System.Drawing; using System.Windows.Forms; namespace ProgrammingWinApps { public class ScrollBars : Form { Panel pnl; PictureBox pb; HScrollBar hbar; VScrollBar vbar; Image img; public ScrollBars( ) { Text = "ScrollBars"; Size = new Size(480,300); // Get an image to use img = Image.FromFile( "TabControlSizeMode.gif"); pnl = new Panel( ); pnl.Parent = this; pnl.Size = new Size(400,200); pnl.Location = new Point(10,10); pnl.BorderStyle = BorderStyle.FixedSingle; pb = new PictureBox( ); pb.Parent = pnl; pb.Size = new Size(img.Size.Width, img.Size.Height); pb.Location = new Point((pnl.Size.Width / 2) - (pb.Size.Width / 2), (pnl.Size.Height / 2) - (pb.Size.Height / 2)); pb.SizeMode = PictureBoxSizeMode.CenterImage; pb.Image = img; // Horizontal scrollbar hbar = new HScrollBar( ); hbar.Parent = this; hbar.Location = new Point(pnl.Left, pnl.Bottom + 25); hbar.Size = new Size(pnl.Width, 25); hbar.Minimum = 0; hbar.Maximum = 100; hbar.SmallChange = 1; hbar.LargeChange = 10; hbar.Value = (hbar.Maximum - hbar.Minimum) / 2; hbar.ValueChanged += new EventHandler(hbar_OnValueChanged); // Vertical scrollbar vbar = new VScrollBar( ); vbar.Parent = this; vbar.Location = new Point(pnl.Right + 25, pnl.Top); vbar.Size = new Size(25, pnl.Height); vbar.Minimum = 0; vbar.Maximum = 100 + vbar.LargeChange; vbar.SmallChange = 1; vbar.LargeChange = 10; vbar.Value = (vbar.Maximum - vbar.Minimum) / 2; vbar.ValueChanged += new EventHandler(vbar_OnValueChanged); } // close for constructor private void hbar_OnValueChanged(object sender, EventArgs e) { pb.Location = new Point((pnl.Size.Width - img.Size.Width) * hbar.Value/( hbar.Maximum - hbar.LargeChange + 1), pb.Top); } private void vbar_OnValueChanged(object sender, EventArgs e) { pb.Location = new Point(pb.Left, (pnl.Size.Height - img.Size.Height) * vbar.Value / (vbar.Maximum- vbar.LargeChange + 1)); } static void Main( ) { Application.Run(new ScrollBars( )); } } // close for form class } // close form namespace
Example 13-8. ScrollBars using VB.NET (ScrollBars.vb)
Option Strict On imports System imports System.Drawing imports System.Windows.Forms namespace ProgrammingWinApps public class ScrollBars : inherits Form dim pnl as Panel dim pb as PictureBox dim hbar as HScrollBar dim vbar as VScrollBar dim img as Image public sub New( ) Text = "ScrollBars" Size = new Size(480,300) ' Get an image img = Image.FromFile( _ "TabControlSizeMode.gif") pnl = new Panel( ) pnl.Parent = me pnl.Size = new Size(400,200) pnl.Location = new Point(10,10) pnl.BorderStyle = BorderStyle.FixedSingle pb = new PictureBox( ) pb.Parent = pnl pb.Size = new Size(img.Size.Width, img.Size.Height) pb.Location = new Point( _ CType((pnl.Size.Width / 2) - (pb.Size.Width / 2), integer), _ CType((pnl.Size.Height / 2) - (pb.Size.Height /2), integer)) pb.SizeMode = PictureBoxSizeMode.CenterImage pb.Image = img ' Horizontal scrollbar hbar = new HScrollBar( ) hbar.Parent = me hbar.Location = new Point(pnl.Left, pnl.Bottom + 25) hbar.Size = new Size(pnl.Width, 25) hbar.Minimum = 0 hbar.Maximum = 100 hbar.SmallChange = 1 hbar.LargeChange = 10 hbar.Value = CType((hbar.Maximum - hbar.Minimum) / 2, integer) AddHandler hbar.ValueChanged, AddressOf hbar_OnValueChanged ' Vertical scrollbar vbar = new VScrollBar( ) vbar.Parent = me vbar.Location = new Point(pnl.Right + 25, pnl.Top) vbar.Size = new Size(25, pnl.Height) vbar.Minimum = 0 vbar.Maximum = 100 vbar.SmallChange = 1 vbar.LargeChange = 10 vbar.Value = CType((vbar.Maximum - vbar.Minimum) / 2, integer) AddHandler vbar.ValueChanged, AddressOf vbar_OnValueChanged end sub ' close for constructor private sub hbar_OnValueChanged(ByVal sender as object, _ ByVal e as EventArgs) pb.Location = new Point( _ CType((pnl.Size.Width - img.Size.Width) * _ hbar.Value /(hbar.Maximum - hbar.LargeChange + 1), integer), _ pb.Top) end sub private sub vbar_OnValueChanged(ByVal sender as object, _ ByVal e as EventArgs) pb.Location = new Point(pb.Left, _ CType((pnl.Size.Height - img.Size.Height) * _ vbar.Value / (vbar.Maximum - vbar.LargeChange + 1), integer)) end sub public shared sub Main( ) Application.Run(new ScrollBars( )) end sub end class end namespace
The programs in Example 13-7 and Example 13-8 start by declaring several controls as member variables so they will be available to the event handler methods as well as the constructor.
In the constructor, the image is instantiated with the static Image.FromFile method. The syntax used here assumes that the image file is located in the current directory, but you can also include a full path to the image file if that is not the case.
The panel control is instantiated, sized to 400 pixels wide by 200 pixels high, and located 10 pixels down and to the right of the upper-left corner of the form client area in the next several lines of code. The border style of the panel is set to FixedSingle.
Next, the picture box control is instantiated. Its Parent property is set to be the panel: the picture box is contained by the panel.
pb.Parent = pnl;
The Size property of the picture box is set by using the size of the image.
pb.Size = new Size(img.Size.Width, img.Size.Height);
The initial location of the picture box is set to be centered about the panel so you initially see the middle of the image:
pb.Location = new Point((pnl.Size.Width / 2) - (pb.Size.Width / 2), (pnl.Size.Height / 2) - (pb.Size.Height / 2));
pb.Location = new Point( _ CType((pnl.Size.Width / 2) - (pb.Size.Width / 2), integer), _ CType((pnl.Size.Height / 2) - (pb.Size.Height /2), integer))
Using the actual numbers from this example, where the panel is 400 x 200 and the image is 990 x 380, the Location property would calculate to:
pb.Location = = new Point(-295, -90);
This puts the upper-left corner of the image up and to the left of the upper-left corner of the panel (out of the visible client area of the panel).
The SizeMode property of the panel is set to CenterImage to clip the image on all four sides and avoid distorting the image size.
Finally, the Image property of the panel is set to the previously instantiated image object.
The two scrollbars are instantiated and specified similarly to each other. The Parent property of both is set to the form. The Location property of the horizontal scrollbar is aligned with the left edge of the panel and 25 pixels below the bottom of the panel.
hbar.Location = new Point(pnl.Left, pnl.Bottom + 25);
The vertical scrollbar is located 25 pixels to the right of the panel and aligned with the top of the panel.
vbar.Location = new Point(pnl.Right + 25, pnl.Top);
Both scrollbars are sized equal to the appropriate edge of the panel and 25 pixels thick.
hbar.Size = new Size(pnl.Width, 25); vbar.Size = new Size(25, pnl.Height);
The minimum values are set to zero, and the maximum values are set to 100. Remember that the value of the scrollbar is the position of the left edge of the thumb on a horizontal scrollbar or the top edge of the thumb in a vertical scrollbar. This will be accounted for in the event handlers, which process the new location of the thumbs.
The SmallChange and LargeChange properties are set to 1 and 10, respectively. The SmallChange property is the change in Value if the user clicks on the scroll arrow or presses an arrow key on the keyboard. The LargeChange property is the change in Value if the user clicks on the scrollbar in the space between the thumb and the scroll arrow or presses the Page Up or Page Down key on the keyboard.
Using the these values for the Minimum, Maximum, SmallChange, and LargeChange properties makes a small change equal to 1 percent of the image size and a large change equal to 10 percent of the image size, results dictated by the choice of values of these properties. If the scrollbar were controlling a text document, on the other hand, you might want to set the Maximum property of a vertical scrollbar to the number of lines contained by the document, the SmallChange property equal to the space taken up by one line, and the LargeChange to the space taken up by one full screen.
The size of the thumb is controlled by the ratio of the LargeChange property to the Maximum property. It indicates the proportion of the image that is visible at one time.
The initial Value of each scrollbar is set to the midpoint between the Maximum value and Minimum value.
Finally, for each scrollbar, an event handler method is added to the delegate for the ValueChanged event. All the action takes place in the event handler.
The two event handler methods, hbar_OnValueChanged and vbar_OnValueChanged, are symmetrically similar. The ValueChanged event is fired every time the Value of the scrollbar is changed, either by user action or programmatically. Although the EventArgs event argument carries no additional information (unlike the Scroll event), the current ScrollBar properties reflect the post-change state. This allows the Location of the panel box to be reset based on the current Value of the scrollbar. The horizontal scrollbar is done with this line of code:
pb.Location = new Point((pnl.Size.Width - img.Size.Width) * hbar.Value/(hbar.Maximum - hbar.LargeChange + 1), pb.Top);
pb.Location = new Point( _ CType((pnl.Size.Width - img.Size.Width) * _ hbar.Value /(hbar.Maximum - hbar.LargeChange + 1), integer), _ pb.Top)
For the vertical scrollbar, it is:
pb.Location = new Point(pb.Left, (pnl.Size.Height - img.Size.Height) * vbar.Value / (vbar.Maximum- vbar.LargeChange + 1));
pb.Location = new Point(pb.Left, _ CType((pnl.Size.Height - img.Size.Height) * _ vbar.Value / (vbar.Maximum - vbar.LargeChange + 1), integer))
Any number of algorithms use the Value of the scrollbar in conjunction with other properties to achieve the desired goal of an application. In a text handling application, for example, the goal would be to position the document based on the total number of lines of text, the number of lines visible at one time, and the font.
In this image handling example, the goal is to set the picture box's Location property. In the horizontal event handler, the vertical dimension of the Location property is unchanged, but the horizontal dimension is calculated. Conversely, for the vertical scrollbar, the horizontal dimension is unchanged while the vertical dimension is calculated.
The calculation treats the difference between the relevant properties (Width or Height) as an offset from zero in each dimension of the Point structure that is the Location. This offset is multiplied by a scaling factor that is the relative position of the thumb. The relative position is the current position, or Value, divided by the total numeric length of the scrollbar. Although the Maximum property is the true total length of the scrollbar, it is not the highest value achievable by a user. The highest achievable value of the scrollbar is the Maximum value minus the thickness of the thumb, which is the LargeChange property, plus one to compensate for zero-based indexing.
In other words, the offset is multiplied by the relative position of the thumb, 0 percent to 100 percent. That is why the Minimum and Maximum value of the scrollbars were set to 0 and 100, respectively.
So instead of a scaling factor of the form:
hbar.Value/ hbar.Maximum
you have a scaling factor of the form:
hbar.Value/( hbar.Maximum- hbar.LargeChange + 1)
13.4.2 As a Parameter Adjuster
Scrollbars can be used to adjust parameters as well as adjust position, as the following example will demonstrate. The program listed in Example 13-9 (in C#, the VB.NET version is nearly identical) is a modified version of the program presented in the previous section in Example 13-7 and Example 13-8. Instead of adjusting the position of a picture box in a panel, this example stretches or shrinks an image either horizontally or vertically.
When the programs are compiled and run, they look like the screenshot shown in Figure 13-12.
Figure 13-13. Scrollbars to adjust parameters
In this example, the image file has an original size of 151 by 140 pixels. It is centered in a picture box that is centered in a panel. As each scrollbar thumb is moved by the user, the image is stretched or shrunk in that direction.
The lines of code in Example 13-9 that differ from the version presented in the previous section (Example 13-7 in C# and Example 13-8 in VB.NET) are highlighted. An analysis follows the code listing.
Example 13-9. ScrollBar as parameter adjuster in C# (ScrollBars-param.cs)
using System; using System.Drawing; using System.Windows.Forms; namespace ProgrammingWinApps { public class ScrollBars : Form { Panel pnl; PictureBox pb; HScrollBar hbar; VScrollBar vbar; public ScrollBars( ) { Text = "ScrollBars - Parameter Adjust"; Size = new Size(480,500); // Get an image to use Image img = Image.FromFile( "Dan at Vernal Pool.gif"); pnl = new Panel( ); pnl.Parent = this; pnl.BorderStyle = BorderStyle.FixedSingle; pnl.Size = new Size(400,400); pnl.Location = new Point(10,10); pb = new PictureBox( ); pb.Parent = pnl; pb.Size = new Size(200, 200); pb.Location = new Point((pnl.Size.Width / 2) - (pb.Size.Width / 2), (pnl.Size.Height / 2) - (pb.Size.Height /2)); pb.BorderStyle = BorderStyle.FixedSingle; pb.SizeMode = PictureBoxSizeMode.StretchImage; pb.Image = img; // Horizontal scrollbar hbar = new HScrollBar( ); hbar.Parent = this; hbar.Location = new Point(pnl.Left, pnl.Bottom + 25); hbar.Size = new Size(pnl.Width, 25); hbar.Minimum = 25; hbar.Maximum = 400; hbar.SmallChange = 10; hbar.LargeChange = 100; hbar.Value = pb.Width - hbar.LargeChange; hbar.ValueChanged += new EventHandler(hbar_OnValueChanged); // Vertical scrollbar vbar = new VScrollBar( ); vbar.Parent = this; vbar.Location = new Point(pnl.Right + 25, pnl.Top); vbar.Size = new Size(25, pnl.Height); vbar.Minimum = 25; vbar.Maximum = 400; vbar.SmallChange = 10; vbar.LargeChange = 100; vbar.Value = pb.Height - vbar.LargeChange; vbar.ValueChanged += new EventHandler(vbar_OnValueChanged); } // close for constructor private void hbar_OnValueChanged(object sender, EventArgs e) { pb.Size = new Size(hbar.Value + hbar.LargeChange - 1, pb.Height); SetLocation( ); } private void vbar_OnValueChanged(object sender, EventArgs e) { pb.Size = new Size(pb.Width, vbar.Value + vbar.LargeChange - 1); SetLocation( ); } private void SetLocation( ) { pb.Location = new Point((pnl.Size.Width / 2) - (pb.Size.Width / 2), (pnl.Size.Height / 2) - (pb.Size.Height /2)); } static void Main( ) { Application.Run(new ScrollBars( )); } } // close for form class } // close form namespace
This example starts off like the previous example, except the image object is not declared as a class member, since it is not referenced outside the constructor. The image instantiated and assigned to the picture box is smaller than the previous example, 151 by 140 pixels, but more importantly, it is smaller than the panel that holds the picture box that contains the image.
To keep the image centered at all times and force the four sides to be clipped evenly, the SizeMode property of the picture box is set to PictureBoxSizeMode.StretchImage.
The Minimum, Maximum, SmallChange, LargeChange, and initial Value properties of the scrollbars are all different from the previous example. In this example, the Maximum is set to 400 to correspond to the size of the panel. The Minimum is set to 25, only because it seemed that going any smaller was pointless. The initial Value property must consider the width of the thumb.
Again, all the action takes place in the event handlers. The Size property of the picture box is set by changing the dimension corresponding to the scrollbar only; the other dimension remains unchanged. A helper method, SetLocation, is then called to set the Location property of the picture box, centering it in the panel, based on the new dimensions of the picture box.
Windows Forms and the .NET Framework
Getting Started
Visual Studio .NET
Events
Windows Forms
Dialog Boxes
Controls: The Base Class
Mouse Interaction
Text and Fonts
Drawing and GDI+
Labels and Buttons
Text Controls
Other Basic Controls
TreeView and ListView
List Controls
Date and Time Controls
Custom Controls
Menus and Bars
ADO.NET
Updating ADO.NET
Exceptions and Debugging
Configuration and Deployment