Up-Down Controls

Up-down controls consist of an edit box with an associated set of up and down arrows. The user can cycle through a range of values in the edit box by clicking on the arrows, or, if the ReadOnly property is not set to true, you can enter values in the edit box directly.

Up-down controls used to be known in other languages as spin controls or spinners.

Most up-down control functionality comes from the UpDownBase class. This is an abstract/MustInherit class; it is not instantiated directly. The .NET Framework provides two classes derived from UpDownBase: NumericUpDown and DomainUpDown. The NumericUpDown control displays numeric values in the edit box, and the DomainUpDown control displays string values representing members of a collection of objects.

In addition to the properties inherited from Control and other base classes, such as ScrollableControl, UpDownBase has other properties, the most commonly used of which are listed in Table 13-16.

Table 13-16. UpDownBase properties


Value type




Read/write. Valid values are members of the BorderStyle enumeration, listed in Table 13-9.



Read/write. If true (the default), user can use up and down arrow keys to select values when the control has focus.



Read-only. Height of control in pixels, based on PreferredHeight property of the text box portion of the control, adjusted for border style.



Read/write. If false (the default), text in control can be edited by the user. If true, text can be changed using the up and down arrows only.



Read/write. Text displayed in the control.



Read/write. Valid values are members of the HorizontalAlignment enumeration, listed in Table 13-17. Default is HorizontalAlignment.Left.



Read/write. The side of the edit box on which the up and down arrows are placed. Valid values are either LeftRightAlignment.Left or LeftRightAlignment.Right (the default).

Table 13-17. HorizontalAlignment enumeration values




Aligned in the center of the control.


Aligned to the left of the control.


Aligned to the right of the control.

13.6.1 NumericUpDown

The NumericUpDown class displays a numeric value in the edit box. This number is incremented or decremented by the value of the Increment property every time the user clicks on one of the arrows or presses an arrow key on the keyboard (if the InterceptArrowKeys property is set to its default value of true).

If the user can enter values directly into the edit box, such as when the control is not set for ReadOnly, then validation automatically occurs when a new value is entered. The new value entered must be a number between the Minimum value and the Maximum value. If the entered value is outside the range of valid numbers, it will automatically be changed to either the Maximum or the Minimum value, depending on which property is exceeded.

Although this automatic validation saves you from writing your own validation code, it may lead to data-entry errors. Out of bounds values will be changed to another, still erroneous, value, and the user could easily miss this.

A ValueChanged event is raised every time the Value property of the numeric up-down is changed, either by user interaction or programmatically. This event, with its event argument of type EventArgs (which has no additional properties), can be trapped and handled, as will be demonstrated shortly.

The commonly used properties of the NumericUpDown control that are not derived from UpDownBase are listed in Table 13-18.

Table 13-18. NumericUpDown properties


Value type




Read/write. Number of decimal places displayed. Default is zero.



Read/write. If true, displays value in hexadecimal format. Default is false.



Read/write. Value to increment or decrement the Value property when an arrow is clicked or an arrow key is pressed. Default is 1.



Read/write. The maximum value of the Value property. Default is 100. Use Decimal.MaxValue (equal to 296 or approximately 7.9 x 1028) to effectively remove the upper bound.



Read/write. The minimum value of the Value property. Default is zero. Use Decimal.MinValue (equal to -296 or approximately -7.9 x 1028) to effectively remove the lower bound.



Read/write. If true, a thousands separator is displayed in the edit box, when appropriate. The separator to be used is determined by system settings. The default value is false.



Read/write. The numeric value displayed by the control.

The programs listed in Example 13-11 (in C#) and in Example 13-12 (in VB.NET) demonstrate the use of a numeric up-down to complement the scrollbars used in Example 13-9 for scaling an image. In this example, which is nearly identical to the previous scrollbar and track bar examples, a numeric up-down control is added to the form, which scales the image uniformly in both directions.

When the code in either example is compiled and run, it looks like the screenshot shown in Figure 13-14.

The lines of code that differ from the previous examples are highlighted. An analysis of the code follows the listings.

Figure 13-15. NumericUpDown control


Example 13-11. NumericUpDown control in C# (NumericUpDowns.cs)


using System;
using System.Drawing;
using System.Windows.Forms;
namespace ProgrammingWinApps
 public class NumericUpDowns : Form
 Panel pnl;
 PictureBox pb;
 HScrollBar hbar;
 VScrollBar vbar;
 NumericUpDown nupdwn;
 Image img;
 public NumericUpDowns( )
 Text = "ScrollBars - NumericUpDowns";
 Size = new Size(480,580);
 // Get an image to use
 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.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.ValueChanged += new EventHandler(vbar_OnValueChanged);
 // NumericUpDown & Label
 Label lbl = new Label( );
 lbl.Parent = this;
 lbl.Text = "Scale Factor:";
 lbl.Location = new Point(
 (((pnl.Right - pnl.Left) / 2) - 75),hbar.Bottom + 25) ;
 lbl.AutoSize = true;
 nupdwn = new NumericUpDown( );
 nupdwn.Parent = this;
 nupdwn.Location = new Point(
 (pnl.Right - pnl.Left) / 2, hbar.Bottom + 25);
 nupdwn.Size = new Size(60,20);
 nupdwn.Value = 1;
 nupdwn.Minimum = -10;
 nupdwn.Maximum = 10;
 nupdwn.Increment = .25m; // decimal
 nupdwn.DecimalPlaces = 2;
 nupdwn.ReadOnly = true;
 nupdwn.TextAlign = HorizontalAlignment.Right;
 nupdwn.ValueChanged += new EventHandler(nupdwn_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));
 private void nupdwn_OnValueChanged(object sender, EventArgs e)
 hbar.Value = Math.Max(hbar.Minimum, 
 img.Width + (int)(( nupdwn.Value / 10) * 
 ((hbar.Maximum - hbar.Minimum) / 2) ));
 vbar.Value = Math.Max(vbar.Minimum, 
 img.Height + (int)(( nupdwn.Value / 10) * 
 ((vbar.Maximum - vbar.Minimum) / 2) ));
 static void Main( ) 
 Application.Run(new NumericUpDowns( ));
 } // close for form class
} // close form namespace

Example 13-12. NumericUpDown control in VB.NET (NumericUpDowns.vb)


Option Strict On
imports System
imports System.Drawing
imports System.Windows.Forms
namespace ProgrammingWinApps
 public class NumericUpDowns : inherits Form
 dim pnl as Panel
 dim pb as PictureBox
 dim hbar as HScrollBar
 dim vbar as VScrollBar
 dim nupdwn as NumericUpDown
 dim img as Image
 public sub New( )
 Text = "ScrollBars - NumericUpDowns"
 Size = new Size(480,580)
 ' Get an image 
 img = Image.FromFile( _
 "Dan at Vernal Pool.gif")
 pnl = new Panel( )
 pnl.Parent = me
 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( _
 CType((pnl.Size.Width / 2) - (pb.Size.Width / 2),integer), _
 CType((pnl.Size.Height / 2) - (pb.Size.Height /2),integer))
 pb.BorderStyle = BorderStyle.FixedSingle
 pb.SizeMode = PictureBoxSizeMode.StretchImage
 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 = 25
 hbar.Maximum = 400
 hbar.SmallChange = 10
 hbar.LargeChange = 100
 hbar.Value = pb.Width
 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 = 25
 vbar.Maximum = 400
 vbar.SmallChange = 10
 vbar.LargeChange = 100
 vbar.Value = pb.Height
 AddHandler vbar.ValueChanged, AddressOf vbar_OnValueChanged
 ' NumericUpDown & Label
 dim lbl as new Label( )
 lbl.Parent = me
 lbl.Text = "Scale Factor:"
 lbl.Location = new Point( _
 CType((pnl.Right - pnl.Left) / 2, integer) - 75, _
 hbar.Bottom + 25) 
 lbl.AutoSize = true
 nupdwn = new NumericUpDown( )
 nupdwn.Parent = me
 nupdwn.Location = new Point( _
 CType((pnl.Right - pnl.Left) / 2, integer), _
 hbar.Bottom + 25)
 nupdwn.Size = new Size(60,20)
 nupdwn.Value = 1
 nupdwn.Minimum = -10
 nupdwn.Maximum = 10
 nupdwn.Increment = .25d ' decimal
 nupdwn.DecimalPlaces = 2
 nupdwn.ReadOnly = true
 nupdwn.TextAlign = HorizontalAlignment.Right
 AddHandler nupdwn.ValueChanged, AddressOf nupdwn_OnValueChanged
 end sub ' close for constructor
 private sub hbar_OnValueChanged(ByVal sender as object, _
 ByVal e as EventArgs)
 pb.Size = new Size(hbar.Value + hbar.LargeChange - 1, pb.Height)
 SetLocation( )
 end sub
 private sub vbar_OnValueChanged(ByVal sender as object, _
 ByVal e as EventArgs)
 pb.Size = new Size(pb.Width, vbar.Value + vbar.LargeChange - 1)
 SetLocation( )
 end sub
 private sub SetLocation( )
 pb.Location = new Point( _
 CType((pnl.Size.Width / 2) - (pb.Size.Width / 2), integer), _
 CType((pnl.Size.Height / 2) - (pb.Size.Height /2), integer))
 end sub
 private sub nupdwn_OnValueChanged(ByVal sender as object, _
 ByVal e as EventArgs)
 hbar.Value = Math.Max(hbar.Minimum, _ 
 img.Width + CType((nupdwn.Value / 10) * _
 ((hbar.Maximum - hbar.Minimum) / 2), integer))
 vbar.Value = Math.Max(vbar.Minimum, _
 img.Height + CType((nupdwn.Value / 10) * _
 ((vbar.Maximum - vbar.Minimum) / 2), integer))
 end sub
 public shared sub Main( ) 
 Application.Run(new NumericUpDowns( ))
 end sub
 end class
end namespace

The programs listed in Example 13-11 and Example 13-12 start off by declaring several controls as member variables. For these examples, the NumericUpDown and the Image variables are declared as member variables rather than in the constructor, because they are referenced throughout the code.

Inside the constructor, the form size is increased in the vertical direction and the Image object is instantiated from the file that was used before. The panel, picture box, and scrollbars are identical to those in Figure 13-12.

Two new controls are added to the constructor: a NumericUpDown, and an associated Label that acts as a caption. The Location property of both controls are calculated to center them below the horizontal scrollbar.

Several properties of the up-down control are set, including the initial Value, the Minimum and Maximum values, the Increment, and the DecimalPlaces. Notice the type character appended to the Increment property value, which is of type Decimal number. It is:


nupdwn.Increment = .25d;


nupdwn.Increment = .25m

The ReadOnly property of the up-down control is set to true so the user cannot enter or modify the value other than through arrows or an arrow key. In the screenshot in Figure 13-14, you can see that this makes the edit box portion of the control appear with a gray background. If the field were editable (if ReadOnly were set to false), then the edit box would be the default background color (typically white, unless the BackColor property were set otherwise).

The TextAlign property is set to a value of HorizontalAlignment.Right to align the text to the right edge of the edit box.

The last line of code in the constructor assigns an event handler method to the event delegate for the ValueChanged event. This event handler, nupdwn_OnValueChanged, is called every time the Value property of the control is changed.

The event handler method achieves the desired effect by setting the Value properties of the scrollbars. Since the picture box control has its SizeMode property set to PictureBoxSizeMode.StretchImage, the original display of the image stretches the 151 x 140 pixel image to 200 x 200, which is slightly distorted because it appears longer than it should. As soon as the up-down control value is changed, the scrollbars are set so that the image is correctly proportioned again.

13.6.2 DomainUpDown

The DomainUpDown control displays a collection of strings, which can represent objects. Items can be added or removed from the collection by using the Add or Remove methods, respectively. If the ReadOnly property is set to false (the default), then a string typed in the edit box must match the string representation of an item already in the collection in order to be accepted.

Like the NumericUpDown control, an event, SelectedItemChanged, is raised when a different item is selected. If the control's Items collection contains objects, then the SelectedItem property can be used as a reference to the selected object in the event handler method. If the Items collection contains strings, then the SelectedItem property will contain the selected string.

The commonly used properties of the DomainUpDown control that are not derived from UpDownBase are listed in Table 13-19.

Table 13-19. DomainUpDown properties


Value type




Read-only. Collection of objects assigned to the control. Items can be added by using the Add or Insert methods.



Read/write. Zero-based index of selected item. If the user has entered a value or if no item is selected, the value is -1. Default value is -1.



Read/write. Currently selected item.



Read/write. If false (the default), the item collection is not sorted. If true, the collection is sorted alphabetically.



Read/write. If true, the list appears to continuously loop from the bottom to the top, and vice versa. The default is false.

The code listings in Example 13-13 (in C#) and in Example 13-14 (in VB.NET) are an extension of the previous example, adding a domain up-down control to change the border style of the panel. They show only the additional code required in Example 13-11 and Example 13-12 to add a DomainUpDown control. The additional control has its Items collection populated with the members of the BorderStyle enumeration (not with strings representing the members of the enumeration). Whenever the up-down control is changed, the SelectedItemChanged event is handled, which applies the currently displayed style to the panel control.

When these programs are compiled and run, they produce the form shown in Figure 13-15. An analysis follows the code listings.

Figure 13-16. DomainUpDown control


Example 13-13. DomainUpDown code delta from Example 13-11 in C# (DomainUpDowns.cs)


DomainUpDown dupdwn;
public DomainUpDowns( )
 // DomainUpDown & Label
 lbl = new Label( );
 lbl.Parent = this;
 lbl.Text = "BorderStyle:";
 lbl.Location = new Point(
 (((pnl.Right - pnl.Left) / 2) - 75),hbar.Bottom + 50) ;
 lbl.AutoSize = true;
 dupdwn = new DomainUpDown( );
 dupdwn.Parent = this;
 dupdwn.Location = new Point(
 (pnl.Right - pnl.Left) / 2, hbar.Bottom + 50);
 dupdwn.Size = new Size(150,dupdwn.PreferredHeight);
 dupdwn.ReadOnly = true;
 dupdwn.TextAlign = HorizontalAlignment.Center;
 dupdwn.UpDownAlign = LeftRightAlignment.Left;
 dupdwn.Wrap = true;
 dupdwn.SelectedItemChanged += 
 new EventHandler(dupdwn_OnSelectedItemChanged);
 // Create the collection of items 
 dupdwn.SelectedIndex = 0; // zero-based index
} // close for constructor
private void dupdwn_OnSelectedItemChanged(object sender, EventArgs e)
 pnl.BorderStyle = (BorderStyle)dupdwn.SelectedItem;

Example 13-14. DomainUpDown code delta from Example 13-12 in VB.NET (DomainUpDowns.vb)


dim dupdwn as DomainUpDown
public sub New( )
 Text = "ScrollBars - DomainUpDowns"
 ' DomainUpDown & Label
 lbl = new Label( )
 lbl.Parent = me
 lbl.Text = "BorderStyle:"
 lbl.Location = new Point( _
 (((pnl.Right - pnl.Left) / 2) - 75),hbar.Bottom + 50) 
 lbl.AutoSize = true
 dupdwn = new DomainUpDown( )
 dupdwn.Parent = me
 dupdwn.Location = new Point( _
 CType(((pnl.Right - pnl.Left) / 2), integer), _
 hbar.Bottom + 50)
 dupdwn.Size = new Size(150,dupdwn.PreferredHeight)
 dupdwn.ReadOnly = true
 dupdwn.TextAlign = HorizontalAlignment.Center
 dupdwn.UpDownAlign = LeftRightAlignment.Left
 dupdwn.Wrap = true
 AddHandler dupdwn.SelectedItemChanged, _
 AddressOf dupdwn_OnSelectedItemChanged
 ' Create the collection of items 
 dupdwn.SelectedIndex = 0 ' zero-based index
end sub ' close for constructor
private sub dupdwn_OnSelectedItemChanged(ByVal sender as object, _
 ByVal e as EventArgs)
 pnl.BorderStyle = CType(dupdwn.SelectedItem, BorderStyle)
end sub

The programs excerpted in Example 13-13 and Example 13-14 are built up from the NumericUpDown examples in Example 13-11 and Example 13-12. They add a member variable for the DomainUpDown control inside the class declaration, but outside the constructor.

Inside the constructor, two controls are added: the domain up-down control is instantiated, and an accompanying label is declared and instantiated to identify the up-down to the user. The domain up-down resembles the numeric up-down used in the previous example, with some changes.

The size property of the domain up-down uses the PreferredHeight property to set the vertical size of the control, rather than hardcoding in a pixel height.


dupdwn.Size = new Size(150,dupdwn.PreferredHeight);

Again the ReadOnly property is set to true, so the user can change the value only by using the up-down arrows or keyboard arrow keys. This also grays the edit box.

If the ReadOnly property were set to false in these examples, with no other changes made to the code, it would work fine if you used the up-down arrows to change the selected item, but it would crash as soon as you changed the value in the edit box directly. This is because the SelectedIndex property becomes -1 if the user edits the edit box. When the SelectedIndex property is -1, then the Selected-Item property has a null value. However, in the dupdwn_OnSelected-ItemChanged method, the SelectedItem property is referenced. When the program tries to reference the null value, it throws an exception.

You can work around this problem by modifying the dupdwn_OnSelectedItemChanged method as follows:


if (dud.SelectedIndex != -1)
 pnl.BorderStyle = (BorderStyle)dud.SelectedItem;

However, doing so will ignore any changes made directly in the edit box, even if it results in a valid value. Use the ValidateEditText and UpdateEditText methods to validate and process the text entered by the user.

The TextAlign property is set to HorizontalAlignment.Center to align the displayed text in the center of the edit box, and the UpDownAlign property is set to LeftRightAlignment.Left to put the up-down arrows to the left of the edit box rather than the default position on the right. The Wrap property is set to true so that the items in the collection will loop endlessly.

An event handler is added to the SelectedItemChanged event delegate so that the dupdwn_OnSelectedItemChanged method will be called every time the selection is changed. The dupdwn_OnSelectedItemChanged method itself is very simple, consisting of a single line of code. It casts the SelectedItem property back to an object of type BorderStyle and assigns that property to the BorderStyle property of the panel.

In the constructor, the final section of code adds three objects, of type BorderStyle, to the collection of Items that comprise the Items property of the domain up-down. It also sets the SelectedIndex property to 0, which causes the first item in the collection to be displayed. If you failed to set the SelectedIndex property, the control would display initially with nothing visible in the edit box and nothing selected, i.e., the SelectedIndex property would be -1.

Although objects are added to the collection, the control displays string representations of the objects. When the SelectedItem property is retrieved in the dupdwn_OnSelectedItemChanged method, these objects must be cast back to BorderStyle objects because it is a narrowing conversion. However, it is a safe cast because you know that the BorderStyle objects were put into the collection to begin with.

Windows Forms and the .NET Framework

Getting Started

Visual Studio .NET


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


Updating ADO.NET

Exceptions and Debugging

Configuration and Deployment

Programming. NET Windows Applications
Programming .Net Windows Applications
ISBN: 0596003218
EAN: 2147483647
Year: 2003
Pages: 148

Flylib.com © 2008-2020.
If you may any questions please contact us: flylib@qtcs.net