Win32 Common Controls, Part 3: Trackbars, Progress Bars, and Up-Down Controls

Nancy Winnick Cluts
Microsoft Developer Network Technology Group

March 1994

Revised: November 1994 (added new section, "Up-Down Notifications," to include information on UDN_DELTAPOS)
February 1995 (addition of WM_VSCROLL message support; change in "Progress Bar Default Behavior" section)
June 1995 (UDN_DELTAPOS notification is sent to the parent of the control, not to the control)

Click to open or copy the files in the Slider sample application for this technical article.

Click to open or copy the files in the Progress sample application for this technical article.

Click to open or copy the files in the Updown sample application for this technical article.

Abstract

The next release of the Microsoft® Windows® operating system (called Windows 95) presents a new set of common controls to developers of Windows-based applications. These controls are provided in a new dynamic-link library (DLL) called COMCTL32.DLL. The controls allow developers to integrate existing applications into the new Windows 95 shell more thoroughly and seamlessly. COMCTL32.DLL is included with Windows 95 and will also be supported in Win32s® (running on Windows version 3.1) and in Windows NT™. Note that these controls are 32-bit only—they will not be supported in 16-bit Windows environments.

This article describes three new common controls: trackbars, progress bars, and up-down controls. It is the third in a series of articles introducing the new common controls. The other articles in the series cover the following topics:

Parts 2–6 of the series have associated code samples that demonstrate the use of the Win32® common controls.

Warning   The Slider, Progress, and Updown executable files associated with this article were built and tested using the Windows 95 Preliminary Development Kit. The executables will run only on Windows 95; they will not run under Windows 3.1 or Windows NT. If you have Windows 95 installed on your computer but you have problems running these samples, copy the project files to your system using the buttons above, rebuild the project, and run the executables.

Please note that this article is based on preliminary information that is subject to change before the final version of Windows 95.

Trackbars

A trackbar is a horizontal window that is used as a scrolling control. A trackbar contains a slider and tick marks. When the user drags the slider to the left or to the right, the control sends WM_HSCROLL (for horizontal trackbars) and WM_VSCROLL (for vertical trackbars) messages to indicate the change in the position of the slider. The tick marks indicate how many points you can move left or right. Figure 1 shows a simple trackbar with a range of one to ten. You can create a trackbar by using the CreateWindow or CreateWindowEx function and specifying the TRACKBAR_CLASS class name. Once you have created a trackbar, you can set the minimum and maximum positions for the slider, draw tick marks, and set the selection by using trackbar messages.

Figure 1. The anatomy of a trackbar

Trackbar Styles and Default Behavior

Trackbars behave like scroll bars, and many of the styles and notifications for trackbars are similar to those of scroll bars. The styles that you can specify when creating a trackbar are listed in Table 1 below. By default, the tick marks are on the right for vertical trackbars and on the bottom for horizontal trackbars. When a user drags the slider or clicks to the right or left of the slider, the slider moves in the appropriate direction, tick by tick. In other words, scrolling is not continuous—you scroll in increments indicated by the tick marks. For example, a trackbar with 10 ticks and a range of 1–100 allows the user to scroll only in increments of 10. You can change the granularity or frequency of ticks by sending the TBM_SETTICFREQ message.

Table 1. Trackbar Styles

Style Use
TBS_AUTOTICKS Adds tick marks when you set the range on the trackbar via the TBM_SETRANGE message.
TBS_VERT Specifies a vertical trackbar.
TBS_HORZ Specifies a horizontal trackbar.
TBS_TOP Places ticks on the top of a horizontal trackbar.
TBS_BOTTOM Places ticks on the bottom of a horizontal trackbar.
TBS_LEFT Places ticks on the left of a vertical trackbar.
TBS_RIGHT Places ticks on the right of a vertical trackbar.
TBS_BOTH Places ticks on both sides of the trackbar.
TBS_NOTICKS Specifies that no ticks will be placed on the trackbar.
TBS_ENABLESELRANGE Allows you to set the range on the trackbar.

When you select a range for a trackbar, a blue line is drawn in the channel of the trackbar, and two arrows are drawn where the tick marks are placed to indicate the beginning and end of the selection range. The Properties for System Virtual Memory page in the Microsoft® Windows® 95 Control Panel System applet provides an example of this control. You will notice a trackbar showing the amount of virtual memory (see Figure 2). The blue line in the trackbar shows the range of the current settings.

Figure 2. Trackbar with selection range

Creating a Trackbar Window

You can create a simple trackbar window by calling the CreateWindow or CreateWindowEx function, specifying the TRACKBAR_CLASS and a combination of trackbar styles listed above. This window class is registered when the dynamic-link library for Win32 common controls (COMCTL32.DLL) is loaded. You can call the InitCommonControls function to ensure that the DLL is loaded.

The code below creates and initializes a trackbar with standard items such as range, initial position, and selection. The first line of code shows how you would call the function.

// An example of how to call the Create function. This call creates a 
// horizontal trackbar with the ticks on the top. The page size is set 
// to 25, the initial position of the slider is 2, the range is from 1 to
// 50, there will be 10 ticks, the selection (blue line) will go from
// the second tick to the fifth tick, and the trackbar will be redrawn.
hWndTrackBar = MyCreateTrackBar ( hWnd, TBS_AUTOTICKS | TBS_TOP, ID_TRACKBAR,
    hInstance, rclSlider, 25, 2, MAKELONG(1,50), 5, MAKELONG(5,20), TRUE);

// Function that creates a trackbar.
// Parameters:
//    HWND hWndParent - Parent window of the trackbar.
//    DWORD dwStyles - Window styles for the trackbar.
//    WORD wID - ID of the trackbar.
//    HINSTANCE hInst - Current instance.
//    RECT rclTrack - Size and position of the trackbar.
//    LONG lPageSize - Sets the page size; use 0 to ignore.
//    LONG lPosition - Sets the initial position.
//    LONG lRange - Sets the range.
//    LONG lFreq - Sets the frequency of ticks; use 0 to ignore.
//    LONG lSelection - Sets the selection; use 0 to ignore.
//    BOOL bRedraw - Redraw flag: TRUE to redraw; otherwise, FALSE.
//
HWND MyCreateTrackBar ( HWND hWndParent, DWORD dwStyles, WORD wID, HINSTANCE 
   hInst, RECT rclTrack, LONG lPageSize, LONG lPosition, LONG lRange, 
   LONG lFreq, LONG lSelection, BOOL bRedraw)
{
  HWND hWndSlider;

  // Create the trackbar.
  hWndSlider  = CreateWindowEx(
    0L,              // No extended styles.
    TRACKBAR_CLASS,  // Trackbar class name.
    "",              // No default text.
    WS_CHILD | WS_VISIBLE | dwStyles,   // Window styles.
    rclTrack.x, rclTrack.y, rclTrack.cx, rclTrack.cy,   // Size and position.
    hWndParent,      // Parent window handle.
    (HMENU)wID,      // ID for the trackbar.
    hInst,           // Current instance.
    NULL);           // No class data.

  if (hWndSlider == NULL)
    return NULL;

  // Set the page size.
  if (lPageSize)
      SendMessage(hWndSlider, TBM_SETPAGESIZE, 0L, lPageSize);

  // Set the tick frequency.
  if (lFreq)
      SendMessage(hWndSlider, TBM_SETTICFREQ, lFreq, 0L);

  // Set the initial range.
  SendMessage(hWndSlider, TBM_SETRANGE, FALSE, lRange);

  // Set the initial position.
  SendMessage(hWndSlider, TBM_SETPOS, FALSE, lPosition);

  // Set the selection.
  if (lSelection)
      SendMessage(hWndSlider, TBM_SETSEL, FALSE, lSelection);

  // Redraw if specified.
  if (bRedraw)
  {
      InvalidateRect(hWndSlider, NULL, TRUE);
      UpdateWindow(hWndSlider);
  }

  return hWndSlider;
}

I created a small sample called Slider that demonstrates the different trackbar styles and how they work. Slider illustrates three trackbars:

In the Slider application, the status bar at the bottom of the screen indicates the current notification being sent to the trackbar that has the keyboard focus.

Warning   The Slider executable file was built and tested using the Windows 95 Preliminary Development Kit. The executable will run only on Windows 95; it will not run under Windows 3.1 or Windows NT. If you have Windows 95 installed on your machine but you have problems running this sample, copy the project files to your system, rebuild the project, and run the executable.

Click to open or copy the Slider project files.

Trackbar Notification Messages

A trackbar notifies its parent window of user actions by sending WM_HSCROLL and WM_VSCROLL messages. If you are familiar with scroll bar notifications, you will be very comfortable working with trackbar notifications, which are very similar.

Table 2 below lists the possible notifications that are sent to trackbar windows. In each case, the low-order word of lParam specifies the position of the slider, and the high-order word is the handle of the trackbar. The wParam parameter specifies the action.

Table 2. Trackbar Notification Messages

Message Description
TB_BOTTOM The user has pressed the end key on the keyboard.
TB_ENDTRACK The user has released the mouse after dragging the slider.
TB_LINEDOWN The user has pressed either down arrow or right arrow on the keyboard. Horizontal trackbars that receive this notification scroll to the right by default.
TB_LINEUP The user has pressed either up arrow or left arrow on the keyboard. Horizontal trackbars that receive this notification scroll to the left by default.
TB_PAGEDOWN The user has clicked to the right of the slider on a horizontal trackbar, has clicked below the slider on a vertical trackbar, or has pressed the pagedown key on the keyboard.
TB_PAGEUP The user has clicked to the left of the slider on a horizontal trackbar, has clicked above the slider on a vertical trackbar, or has pressed the pageup key on the keyboard.
TB_THUMBPOSITION The trackbar should move to the absolute position specified by the high-order word of wParam (nPos).
TB_THUMBTRACK The user is dragging the slider.
TB_TOP The user has pressed to home key on the keyboard.

Trackbar Messages

This section lists the messages that you can send to trackbar windows to add or remove tick marks, to set or clear selections, and to make other changes.

TBM_CLEARSEL

wParam = (BOOL)fRedraw;       \\ TRUE to redraw after selection is cleared
lParam = 0;                   \\ not used

Description: The TBM_CLEARSEL message clears the current selection in a trackbar.

Parameters: If wParam is TRUE, the trackbar is redrawn after the selection is cleared. lParam is not used.

Return value: None.

TBM_CLEARTICS

wParam = (BOOL)fRedraw;     \\ TRUE to redraw after tick marks are cleared
lParam = 0;                 \\ not used

Description: The TBM_CLEARTICS message removes the tick marks from a trackbar.

Parameters: If wParam is TRUE, the trackbar is redrawn after the tick marks are cleared. lParam is not used.

Return value: None.

TBM_GETCHANNELRECT

wParam = 0;                       \\ not used
lParam = (LPRECT)lprc;            \\ bounding rectangle

Description: The TBM_GETCHANNELRECT message gets the rectangle bounding the channel that the slider slides in.

Parameters: wParam is not used. lParam is a pointer to the bounding rectangle for the channel.

Return value: None.

TBM_GETLINESIZE

wParam = 0;                       \\ not used
lParam = 0;                       \\ not used

Description: The TBM_GETLINESIZE message gets the amount to move the slider when the user presses the lineup or linedown key. The default increment is one tick.

Parameters: Not used.

Return value: The current line size.

TBM_GETNUMTICS

wParam = 0;                       \\ not used
lParam = 0;                       \\ not used

Description: The TBM_GETNUMTICS message gets the number of tick marks in a trackbar.

Parameters: Not used.

Return value: The number of tick marks.

TBM_GETPAGESIZE

wParam = 0;                       \\ not used
lParam = 0;                       \\ not used

Description: The TBM_GETPAGESIZE message gets the amount to move the slider when the user presses the pageup or pagedown key. The default is calculated to be the difference between the maximum range and the minimum range divided by five.

Parameters: Not used.

Return value: The current page size.

TBM_GETPOS

wParam = 0;                       \\ not used
lParam = 0;                       \\ not used

Description: The TBM_GETPOS message gets the current position of the slider in a trackbar.

Parameters: Not used.

Return value: A 32-bit value that specifies the current position.

TBM_GETPTICS

wParam = 0;                       \\ not used
lParam = 0;                       \\ not used

Description: The TBM_GETPTICS message gets the address of the array containing the positions of tick marks for a trackbar.

Parameters: Not used.

Return value: The address of the array.

TBM_GETRANGEMAX

wParam = 0;                       \\ not used
lParam = 0;                       \\ not used

Description: The TBM_GETRANGEMAX message gets the maximum position for the slider in a trackbar.

Parameters: Not used.

Return value: A 32-bit value that specifies the maximum position.

TBM_GETRANGEMIN

wParam = 0;                       \\ not used
lParam = 0;                       \\ not used

Description: The TBM_GETRANGEMIN message gets the minimum position for the slider in a trackbar.

Parameters: Not used.

Return value: A 32-bit value that specifies the minimum position.

TBM_GETSELEND

wParam = 0;                       \\ not used
lParam = 0;                       \\ not used

Description: The TBM_GETSELEND message gets the ending position of the current selection in a trackbar.

Parameters: Not used.

Return value: A 32-bit value that specifies the ending position.

TBM_GETSELSTART

wParam = 0;                       \\ not used
lParam = 0;                       \\ not used

Description: The TBM_GETSELSTART message gets the starting position of the current selection in a trackbar.

Parameters: Not used.

Return value: A 32-bit value that specifies the starting position.

TBM_GETTHUMBRECT

wParam = 0;                       \\ not used
lParam = (LPRECT)lprc;            \\ bounding rectangle for the slider

Description: The TBM_GETTHUMBRECT message gets the rectangle bounding the slider (thumb).

Parameters: wParam is not used. lParam is a pointer to the bounding rectangle for the slider.

Return value: None.

TBM_GETTIC

wParam = iTic;                    \\ zero-based index of the tick mark
lParam = 0;                       \\ not used

Description: The TBM_GETTIC message gets the position of a tick mark in a trackbar.

Parameters: wParam is the zero-based index identifying a tick mark. lParam is not used.

Return value: The position of the specified tick mark. Returns –1 if wParam does not specify a valid index.

TBM_GETTICPOS

wParam = iTic;                    \\ zero-based index of the tick mark
lParam = 0;                       \\ not used

Description: The TBM_GETTICPOS message gets the current physical position of a tick mark in a trackbar.

Parameters: wParam is the zero-based index identifying a tick mark. lParam is not used.

Return value: The physical position, in client coordinates, of the specified tick mark. Returns –1 if wParam does not specify a valid index.

TBM_SETLINESIZE

wParam = 0;                       \\ not used
lParam = iLine;                   \\ the new line size

Description: The TBM_SETLINESIZE message sets the amount to move the slider when the user presses the lineup or linedown key. The default increment is one tick.

Parameters: wParam is not used. lParam is the new line size.

Return value: The previous line size.

TBM_SETPAGESIZE

wParam = 0;                       \\ not used
lParam = iPage;                   \\ the new page size

Description: The TBM_SETPAGESIZE message sets the amount to move the slider when the user presses the pageup or pagedown key. The default is calculated to be the difference between the maximum range and the minimum range divided by five.

Parameters: wParam is not used. lParam is the new page size.

Return value: The previous page size.

TBM_SETPOS

wParam = (BOOL)fPosition;         \\ TRUE to set the position
lParam = (LONG)lPosition;         \\ new slider position

Description: The TBM_SETPOS message sets the current position of the slider in a trackbar.

Parameters: If wParam is TRUE, the message sets the slider to the position given by lParam. Otherwise, the message ensures that the current position is within the current range, but does not move the slider. lParam is the new position of the slider.

Return value: None.

TBM_SETRANGE

wParam = (BOOL)fRedraw;         \\ TRUE to redraw slider after range is set
lParam = MAKELONG(lMin, lMax);  \\ minimum and maximum slider position

Description: The TBM_SETRANGE message sets the minimum and maximum positions for the slider in a trackbar.

Parameters: If wParam is TRUE, the slider is redrawn after the range is set. lParam specifies the minimum and maximum positions for the slider.

Return value: None.

TBM_SETRANGEMAX

wParam = (BOOL)fRedraw;        \\ TRUE to redraw slider after range is set
lParam = lMaximum;             \\ Maximum position for the slider

Description: The TBM_SETRANGEMAX message sets the maximum position for the slider in a trackbar.

Parameters: If wParam is TRUE, the slider is redrawn after the range is set. lParam is the maximum position for the slider.

Return value: None.

TBM_SETRANGEMIN

wParam = (BOOL)fRedraw;           \\ TRUE to redraw after minimum is set
lParam = lMinimum;                \\ minimum position for the slider

Description: The TBM_SETRANGEMIN message sets the minimum position for the slider in a trackbar.

Parameters: If wParam is TRUE, the slider is redrawn after the range is set. lParam is the minimum position for the slider.

Return value: None.

TBM_SETSEL

wParam = (BOOL)fRedraw;           \\ TRUE to redraw after selection is set
lParam = MAKELONG(lStart, lEnd);  \\ start and end selection positions

Description: The TBM_SETSEL message sets the starting and ending positions of the selection in a trackbar.

Parameters: If wParam is TRUE, the slider is redrawn after the selection is set. lParam specifies the starting and ending positions for the selection.

Return value: None.

TBM_SETSELEND

wParam = (BOOL)fRedraw;       \\ TRUE to redraw after end selection is set
lParam = lEnd;                \\ end selection position

Description: The TBM_SETSELEND message sets the position of the end of the selection in a trackbar.

Parameters: If wParam is TRUE, the slider is redrawn after the selection is set. lParam is the ending position for the selection.

Return value: None.

TBM_SETSELSTART

wParam = (BOOL)fRedraw;      \\ TRUE to redraw after start selection is set
lParam = lStart;             \\ start selection position

Description: The TBM_SETSELSTART message sets the starting position of the current selection in a trackbar.

Parameters: If wParam is TRUE, the slider is redrawn after the selection is set. lParam is the starting position of the selection.

Return value: None.

TBM_SETTIC

wParam = 0;                       \\ not used
lParam = (LONG)lPosition;         \\ position of the tick mark

Description: The TBM_SETTIC message sets the position of a tick mark in a trackbar.

Parameters: wParam is not used. lParam is the position of the tick mark. This parameter must specify a positive value.

Return value: TRUE if the tick mark is set; otherwise, returns FALSE.

TBM_SETTICFREQ

wParam = iFrequency;              \\ frequency of ticks to set
lParam = 0;                       \\ not used

Description: The TBM_SETTICFREQ message sets the frequency of ticks in the trackbar.

Parameters: wParam is the desired frequency of ticks. lParam is not used.

Return value: None.

Progress Bars

A progress bar is a window that an application can use to indicate the progress of a lengthy operation. Progress bars are often used in setup or installation programs that copy a large number of files. The application sets the range and current position of the progress bar (similar to a scroll bar) and has the ability to advance the current position. The progress bar can include text that indicates progress either as a percentage of the entire range or as the value of the current position. Figure 3 shows a simple progress bar.

Figure 3. A simple progress bar

Progress Bar Default Behavior

A progress bar displays the system highlight color to indicate the progress of an operation. The progress bar has a range and a current position. The range represents the entire duration of the operation, and the current position represents the progress that the application has made toward completing the operation. The window procedure uses the range and current position to determine the percentage of the progress bar to fill with the highlight color. By default, the minimum range of a progress bar is zero, and the maximum range is 100. The increment value is set to 10.

Creating a Progress Bar Window

You can create a progress bar by using the CreateWindow or CreateWindowEx function and specifying the PROGRESS_CLASS window class. This window class is registered when the dynamic-link library (DLL) for Win32 common controls (COMCTL32.DLL) is loaded. You can call the InitCommonControls function to ensure that the DLL is loaded.

The following code creates a simple progress bar window in the parent's window procedure. A timer is created to send messages to advance the progress bar.

// Function that creates a progress bar.
// Parameters:
//    HWND hWndParent - Parent window of the progress bar.
//    RECT rclPos - Size and position of the progress bar.
//    WORD wID - ID of the progress bar.
//    HINSTANCE hInst - Current instance.
//    LONG lRange - Sets the range.
//    LONG lStep - Sets the stepping.
//
HWND MyCreateProgressBar( HWND hWndParent, RECT rclPos, WORD wID,
      HINSTANCE hInst, LONG lRange, LONG lStep)
(
  HWND hWndProgress;
 
  hWndProgress  = CreateWindowEx(
      OL,
      PROGRESS_CLASS,
      "",
      WS_CHILD | WS_VISIBLE,
      rclPos.x, rclPos.y, rclPos.cx, rclPos,cy,
      hWndParent,         
      (HMENU)wID,         
      hInst,
      NULL);

  if (hWndProgress == NULL)

  // Set the range for the progress bar.
  SendMessage(hWndProgress, PBM_SETRANGE, 0L, lRange);

  // Set the step.
  SendMessage(hWndProgress, PBM_SETSTEP, lStep, 0L);

  return (hWndProgress);
}
.
.
.
RECT rcl;   // Holds size of the progress bar.

switch(message)
{
    case WM_CREATE:
        rcl.x = 10; rcl.y = 100; rcl.cx = 500; rcl.cy = 20;
        hWndProgress = CreateProgressBar( hWnd, rcl, ID_PROGRESS,
             hInst, MAKELONG(0,20),1);
        break;

    case WM_TIMER:
        if (uCurrent < uMax)
        {
            // Increment (step) the progress bar.
            SendMessage(hWndProgress, PBM_STEPIT,0L,0L);
            uCurrent++;
        }
        else
        {
            // We are at the end of the range - kill the timer.
            KillTimer(hWnd, 1000);
            uCurrent = 0;
        }
        break;

    case WM_COMMAND:
        switch( LOWORD( wParam ))
        {

            case IDM_STOP:
               // Stop the progress indicator.
               SendMessage(hWndProgress, PBM_SETPOS,0L,0L);
               KillTimer(hWnd, 1000);
               break;

            case IDM_START:
               uCurrent = uMin;
               SetTimer(hWnd, 1000, 500, NULL);
               break;
        .
        .
        .
    }

I have included a sample called Progress that demonstrates the use of a progress indicator. You can use the button below to see how it works.

Warning   The Progress executable file was built and tested using the Windows 95 Preliminary Development Kit. The executable will run only on Windows 95; it will not run under Windows 3.1 or Windows NT. If you have Windows 95 installed on your computer but you have problems running this sample, copy the project files to your system, rebuild the project, and run the executable.

Click to open or copy the Progress project files.

Progress Bar Messages

This section lists the messages that you can send to progress bar windows. For example, you can adjust the range to convenient integers by using the PBM_SETRANGE message and use the PBM_SETPOS message to set the current position to a specified value. These messages are also documented in the Programmer's Reference for Windows 95.

PBM_DELTAPOS

wParam = nIncrement;              \\ amount to advance the position
lParam = 0;                       \\ not used

Description: The PBM_DELTAPOS message advances the position for a progress bar by the specified increment and redraws the bar.

Parameters: wParam is the amount to advance the position. lParam is not used.

Return value: The previous position.

PBM_SETPOS

wParam = nNewPos;                 \\ the new position
lParam = 0;                       \\ not used

Description: The PBM_SETPOS message sets the position for a progress bar and redraws it to reflect the new position.

Parameters: wParam is the new position. lParam is not used.

Return value: The previous position.

PBM_SETRANGE

wParam = 0;                              \\ not used
lParam = MAKELONG(nMinRange, nMaxRange); \\ minimum and maximum range values

Description: The PBM_SETRANGE message sets the range (minimum and maximum values) for a progress bar and redraws the bar.

Parameters: wParam is not used. lParam specifies the minimum and maximum range values. By default, the minimum value is 0, and the maximum value is 100.

Return value: The previous range values if successful; otherwise, returns 0. The low-order word of the return value specifies the previous minimum value, and the high-order word of the return value specifies the previous maximum value.

PBM_SETSTEP

wParam = nStepInc;                \\ new step increment
lParam = 0;                       \\ not used

Description: The PBM_SETSTEP message specifies the step increment for a progress bar. The step increment is the amount by which the progress bar increases its position whenever it receives a PBM_STEPIT message. By default, the step increment is set to 10.

Parameters: wParam is the new step increment. lParam is not used.

Return value: The previous step increment.

PBM_STEPIT

wParam = 0;                       \\ not used
lParam = 0;                       \\ not used

Description: The PBM_STEPIT message advances the position for a progress bar by the step increment and redraws it. An application sets the step increment by sending the PBM_SETSTEP message. When the position exceeds the maximum range value, this message resets the position so that the progress indicator starts over again from the beginning.

Parameters: Not used.

Return value: The previous position.

Up-Down Controls

An up-down control is a small window that contains up and down arrows that the user can click to increment or decrement a value. An up-down control is similar to a scroll bar, but it consists only of arrows. (It also has a much sillier name.) You can use an up-down control alone as a simplified scroll bar, or with another control (called a buddy control—yet another silly name). In Figure 4, the up-down control is paired with an edit control to create a spin box; however, you can use any other type of control as the designated buddy control. When users click an arrow or use the arrow keys on the keyboard, the up-down control increments or decrements the value in the edit control.

Figure 4. A typical spin box

You can create an up-down control by using the CreateWindow or CreateWindowEx function and specifying UPDOWN_CLASS for the class. To add an up-down control to a dialog box template, specify the UPDOWN_CLASS window class in your template. This window class is registered when the dynamic-link library for Win32 common controls (COMCTL32.DLL) is loaded. You can call the InitCommonControls function to ensure that the DLL is loaded.

The range of an up-down control specifies the upper and lower bound for the position. (The position of an up-down control is the integer the user adjusts by using the up and down arrows. Unlike a scroll bar's position, the position of an up-down control is updated automatically.) The upper bound may be less than the lower bound, in which case the up arrow decrements the position and the down arrow increments it.

Here are some of the things you can do to an up-down control:

Up-Down Styles and Default Behavior

You can specify different window styles to control the characteristics of an up-down control. For example, you can change the way the up-down control positions itself relative to its buddy control, determine whether it sets the text of its buddy control, and determine whether it processes the up arrow and down arrow keys on the keyboard.

Table 3 lists the window styles that you can specify for up-down controls.

Table 3. Up-Down Window Styles

Style Use
UDS_ALIGNLEFT Aligns the up-down control on the left edge of the buddy control. The width of the buddy control is decreased to accommodate the width of the up-down control.
UDS_ALIGNRIGHT Aligns the up-down control on the right edge of the buddy control. The width of the buddy control is decreased to accommodate the width of the up-down control.
UDS_ARROWKEYS Intercepts the up and down arrow keys in the buddy control.
UDS_AUTOBUDDY Sets the buddy control to be the previously created window control.
UDS_HORZ Draws the two buttons of the up-down control side by side. If you do not specify this style, the buttons are drawn one above the other.
UDS_NOTHOUSANDS Specifies that a separator should not be inserted between groups of three digits in the buddy control. By default, the separator is inserted.
UDS_SETBUDDYINT Calls the SetDlgItemInt function to update the buddy control each time the number changes. This is handy if your buddy control is an edit control that is spinning numbers.
UDS_WRAP Wraps back to the opposite limit if the user attempts to exceed the minimum/maximum limit.

Note that some of the styles, such as UDS_SETBUDDYINT, UDS_ALIGNLEFT, and UDS_ALIGNRIGHT, affect the up-down control's buddy control.

By default, the position of the up-down control does not change if the user attempts to increment or decrement it beyond the upper or lower limit. You can change this behavior by using the UDS_WRAP style, which "wraps" the position to the opposite extreme (for example, if your range is 1–10, incrementing the position past 10 wraps it back to 1).

The range of an up-down control may not exceed 32,767 positions. The range may be inverted; that is, the lower limit of the range may be greater than the upper limit. However, note that the up arrow always moves the current position toward the upper limit, and the down arrow always moves the current position toward the lower limit. If the range is zero (that is, the lower limit is equal to the upper limit) or the control is disabled, the control draws grayed arrows for both buttons.

The buddy control must have the same parent as the up-down control. If the buddy control resizes, and the UDS_ALIGNLEFT or UDS_ALIGNRIGHT style is used, you must send the UDM_SETBUDDY message to re-anchor the up-down control on the appropriate border of the buddy control. The UDS_AUTOBUDDY style calls the GetWindow function with GW_HWNDPREV to choose the best buddy control. In the case of a dialog resource, the UDS_AUTOBUDDY style chooses the previous control listed in the resource script. If the z-order of the windows changes, sending a UDM_SETBUDDY message with a NULL handle will cause a new buddy to be selected; otherwise, the original auto-buddy choice is maintained.

The UDS_ARROWKEYS style subclasses the buddy control to capture the VK_UP and VK_DOWN arrow key messages. The UDS_HORZ style draws the two buttons side by side with left and right arrows instead of up and down arrows. The only notifications that an up-down window receives are WM_HSCROLL and WM_VSCROLL notifications. UDS_HORZ sends a WM_HSCROLL message instead of a WM_VSCROLL message.

Creating an Up-Down Window

The following code demonstrates how easy it is to create a spin box as part of a dialog box. Note that the Dialog Editor does not currently support spin boxes, so you will have to add this code after creating your dialog template. When the user chooses OK from the dialog box, the current selection in the spin box is retrieved and saved, and the dialog box is dismissed.

// Function that creates a spin box.
// Parameters:
//    DWORD dwStyles - Window styles for the up-down control.
//    int x,y,cx,cy - Default size and position.
//    HWND hWndParent - Parent window of the up-down control.
//    int nID - ID of the spin box.
//    HINSTANCE hInst - Current instance.
//    HWND hWndBuddy - Handle to the buddy control.
//    int nUpper, nLower, nPos - Sets the range and initial position.
//
HWND MyCreateUpDownControl(DWORD dwStyles, int x, int y, int cx, int cy, 
     HWND hWndParent, int nID, HINSTANCE hInst, HWND hWndBuddy, int nUpper,
     int nLower, int nPos )
{
  HWND hWndUpDown;

  // Create the up-down control.
  hWndUpDown = CreateWindowEx (
                0L,                   // No extended styles.
                UPDOWN_CLASS,         // Up-down window class.
                "",                   // No default text.
                WS_CHILD | WS_BORDER | WS_VISIBLE | dwStyles, // Window styles.
                x, y, cx, cy,         // Size and position.
                hWndParent,           // Handle to parent.
                nID,                  // ID of the up-down control.
                hInst,                // Current instance.
                NULL );               // No control data.

  if (hWndUpDown != NULL)
  {
    // Set the buddy control.
    SendMessage( hWndUpDown, UDM_SETBUDDY, (LONG)hWndBuddy, 0L );

    // Set the range.
    SendMessage( hWndUpDown, UDM_SETRANGE, 0L, MAKELONG(nUpper,nLower));

    // Set the initial positon.
   SendMessage( hWndUpDown, UDM_SETPOS, 0L, MAKELONG( nPos, 0));
  } 
  return (hWndUpDown);
}

BOOL APIENTRY Spin(
    HWND hDlg,
    UINT message,
    UINT wParam,
    LONG lParam)
{
    static HWND hWndUpDown, hWndBuddy;
    BOOL bErr;

    switch (message)
    {
        case WM_INITDIALOG:
            // Get a handle to the edit (buddy) control.
            hWndBuddy = GetDlgItem(hDlg, IDE_BUDDY);
            
            // Create the up-down control.
            hWndUpDown = MyCreateUpDownControl(
                UDS_WRAP | UDS_ARROWKEYS | UDS_ALIGNRIGHT |
                UDS_SETBUDDYINT, 0, 0, 8, 8, hDlg, ID_UPDOWN, hInst, hWndBuddy, 
                MAX_SPIN,MIN_SPIN,1);

            if (hWndUpDown)
                return (TRUE);
            else
                return (FALSE);

        case WM_COMMAND:              
            switch (LOWORD(wParam))
            {
                case IDOK:
                    iNumLines = (int)GetDlgItemInt(hDlg, IDE_BUDDY, 
                        &bErr, FALSE );
                    InvalidateRect(hWndMain, NULL, TRUE); 

                case IDCANCEL:
                    EndDialog(hDlg, TRUE);
                    break;
                
            }
            break;
    
    }
    return (FALSE);   

}

Now that I have shown you the steps for creating an up-down control, I can tell you about a helper function called CreateUpDownControl that you can use to create an up-down control. In fact, you can call the CreateUpDownControl function in the same way that you call MyCreateUpDownControl in the code above.

I have included a sample called UpDown that demonstrates an up-down control used in combination with an edit control. You can click the button below to see how it works.

Warning   The UpDown executable file was built and tested using the Windows 95 Preliminary Development Kit. The executable will run only on Windows 95; it will not run under Windows 3.1 or Windows NT. If you have Windows 95 installed on your computer but you have problems running this sample, copy the project files to your system, rebuild the project, and run the executable.

Click to open or copy the UpDown project files.

Up-Down Messages

You can send messages to an up-down control to get or change its attributes, its buddy control, its range, its position, and so on. An up-down control notifies its parent window whenever its position changes by sending it a WM_VSCROLL message. This section lists the messages that you can send to up-down windows.

UDM_GETACCEL

wParam = nAccels;               \\ number of accelerators to get
lParam = (LPUDACCEL) aAccels;   \\ array that receives the accelerator values

Description: The UDM_GETACCEL message gets information about the accelerators for an up-down control.

Parameters: wParam is the number of accelerators to retrieve. If this value exceeds the number of existing accelerators, the message returns information for existing accelerators only. lParam is the address of an array of UDACCEL structures that contain the information about the accelerators. The array must have the same number of elements as the number of accelerators given by wParam. A UDACCEL structure consists of the following members:

Return value: The current number of accelerators for the control.

UDM_GETBASE

wParam = 0;                       \\ not used
lParam = 0;                       \\ not used

Description: The UDM_GETBASE message gets the current base value for an up-down control.

Parameters: Not used.

Return value: A 32-bit value in which the low-order word specifies the current base value. Returns a value of 10 for decimal and a value of 16 for hexadecimal.

UDM_GETBUDDY

wParam = 0;                       \\ not used
lParam = 0;                       \\ not used

Description: The UDM_GETBUDDY message gets the handle of the current buddy control.

Parameters: Not used.

Return value: A 32-bit value in which the low-order word is the handle of the current buddy control.

UDM_GETPOS

wParam = 0;                       \\ not used
lParam = 0;                       \\ not used

Description: The UDM_GETPOS message gets the current position of an up-down control.

Parameters: Not used.

Return value: The low-order word of the return value indicates the current position. The high-order word is nonzero if an error occurred.

UDM_GETRANGE

wParam = 0;                       \\ not used
lParam = 0;                       \\ not used

Description: The UDM_GETRANGE message gets the upper and lower limits for an up-down control.

Parameters: Not used.

Return value: A 32-bit value that contains the upper and lower limits. The low-order word is the upper limit for the control, and the high-order word is the lower limit.

UDM_SETACCEL

wParam = nAccels;                 \\ number of accelerators to set
lParam = (LPUDACCEL)aAccels;      \\ array of accelerators to set

Description: The UDM_SETACCEL message sets the accelerators for an up-down control.

Parameters: wParam is the number of accelerators to set. lParam is the address of an array of UDACCEL structures that contain information about the accelerators to add. The array must have the same number of elements as the number of accelerators given by wParam.

Return value: TRUE if successful, FALSE otherwise.

UDM_SETBASE

wParam = nBase;                   \\ new base value
lParam = 0;                       \\ not used

Description: The UDM_SETBASE message sets the base value for an up-down control. The base value determines whether the buddy control displays numbers in decimal or hexadecimal digits. Hexadecimal numbers are always unsigned; decimal numbers are signed. If the buddy control is a list box, the up-down control sets its current selection instead of its text.

Parameters: wParam is the new base value for the control. Specify 10 for decimal and 16 for hexadecimal. lParam is not used.

Return value: The previous base value if successful; otherwise, returns zero.

UDM_SETBUDDY

wParam = (HWND)hwndBuddy;         \\ handle of the new buddy control
lParam = 0;                       \\ not used

Description: The UDM_SETBUDDY message sets the buddy control for an up-down control.

Parameters: wParam is the handle of the buddy control. lParam is not used.

Return value: A 32-bit value in which the low-order word is the handle of the previous buddy control.

UDM_SETPOS

wParam = 0;                       \\ not used
lParam = MAKELONG(nPos,0);        \\ new position for the up-down window

Description: The UDM_SETPOS message sets the current position for an up-down control.

Parameters: wParam is not used. The high order word in lParam contains the new position for the control. This value should be in the range specified by the upper and lower limits for the control.

Return value: The previous position.

UDM_SETRANGE

wParam = 0;                         \\ not used
lParam = MAKELONG(nUpper, nLower);  \\ upper and lower limits for the control

Description: The UDM_SETRANGE message sets the upper and lower limits for an up-down control.

Parameters: wParam is not used. lParam specifies the upper and lower limits for the control.

Return value: None.

Up-Down Notifications

The up-down control has one unique notification sent to it in the form of a WM_NOTIFY message. This notification, UDN_DELTAPOS, is sent to the parent of the up-down control when the position of the control is changing (that is, when the user has indicated a change in the value by pressing the up or down arrow). This notification is sent before the actual WM_VSCROLL message is sent. This gives the application a chance to view, change, or disallow the action. If the application wishes to disallow the change in the value, it returns a nonzero value in response to this notification.

The structure passed to the application as part of the WM_NOTIFY message is the NM_UPDOWN structure. This structure consists of the following members:

Summary

Now you know how to create and use three more of the new common controls that are built into Windows 95. You can finally use built-in controls for trackbars, spin boxes, and progress indicators. My next article (Part 4 of this series) will cover two controls that you probably haven't had the chance to implement yourself (isn't that special?): column headers and list view controls.