Scroll Bars


Window Scroll Bars versus Scroll Bar Control Windows

Scroll bars were first encountered in chapter 3, where both vertical and horizontal scroll bars were attached to the client window. These types of scroll bars may be referred to as "Window Scroll Bars" and they are always positioned at the right or the bottom of the window. window scroll bars are included via the styles style::horizontal_scroll and style::vertical_scroll. This chapter deals with scroll bars as child control windows.

Scroll Bar Control windows

When present as a child control window, a scroll bar may appear anywhere within the parent. scroll bars are either vertical or horizontal and are ascribed one of the styles scrollbar_style::vertical or scrollbar_style::horizontal.

Unlike button controls, edit controls and list box controls, a scroll bar control does not use the command message to notify its owner of events. Instead, a scroll bar notifies its owner of events using the message message::horizontal_scroll or message::vertical_scroll (depending upon whether it is a horizontal or vertical scroll bar respectively). For window scroll bars, the handle of the scroll bar is zero when notifying the window of scroll events. For control window scroll bars, the handle of the scroll bar is included in the notification. This allows the notifications of window scroll bars and control window scroll bars to be differentiated.

A vertical window scroll bar is of a fixed width which may be ascertained using the system metric system_metric::vertical_scroll_width. Similarly, a horizontal window scroll bar is of fixed height system_metric::horizontal_scroll_height. The function get_system_metrics may be used to discover these values. Conversely, scroll bar controls may be of variable width and height (for vertical and horizontal styles respectively).

The range, position and information for scroll bar controls (both horizontal and vertical) may be set (as for window scroll bars) using:

The difference is that the function is called for the control window rather than for the containing window (that is, the handle of the control rather than the handle of the parent is supplied to the function call). The other difference is that in each case, the parameter bar should be specified as scrollbar_identity::control for control windows.

Colors of Scroll Bars

The color of the end buttons and slider of a scroll bar are based upon the colors:

The large area between the buttons is based upon the colors:

The color of the large area of the scroll bar may be adjusted by intercepting the notification message message::color_scrollbar.

An Example of Display Controls and Scroll Bar Controls

By way of giving an example of display (static) controls and scroll bar controls, a program that displays colors is the subject of this section. The source file contains code that creates 10 child windows. The handles for these 10 windows are declared in the window data structure - as shown below.

handle scroll_array[3],
       label_array[3],
       value_array[3],
       window_rectangle;

A snapshot of the program in action is shown below.

The left portion of the screen is covered by a static display control - WindowRectangle. Overlaid over this static are nine controls - six display controls and three scroll bar controls. On the right portion of the client, the actual client window is exposed. The client window does not intercept the message message::paint; however, the background brush of the client is adjusted according to the values reported by the scroll bars - as shown below.

case message::vertical_scroll:
 {
   window_data* data = (window_data*)get_window_pointer(window_handle, 0);

   int i = get_window_integer((handle)parameter2,offset::identity);

  switch (low_part(parameter1))
   {
    case scrollbar_notify::page_down:
     data->color_array[i] += 15;
                                  // fall through
    case scrollbar_notify::line_down:
     data->color_array[i] = minimum(255,data->color_array[i] + 1);
     break;

    case scrollbar_notify::page_up:
     data->color_array[i] -= 15;
                                  // fall through
    case scrollbar_notify::line_up:
     data->color_array[i] = maximum(0,data->color_array[i] - 1);
     break;

    case scrollbar_notify::top:
     data->color_array[i] = 0;
     break;

    case scrollbar_notify::bottom:
     data->color_array[i] = 255;
     break;

    case scrollbar_notify::slider_position:
    case scrollbar_notify::slider_track:
     data->color_array[i] = high_part(parameter1);
     break;

    default: break;
   }

  set_scroll_position(scroll_array[i],
                    scrollbar_identity::Control,
                    data->color_array[i],
                    true);

  set_window_text(value_array[i],
                integer_to_string(data->color_array[i]));

  delete_object((handle)set_class_pointer(window_handle,
                                       class_offset::background_brush,
                                       create_solid_brush(red_green_blue(data->color_array[0],
                                                                     data->color_array[1],
                                                                     data->color_array[2]))));

  invalidate_rectangle(window_handle,&ColorRectangle,true);
 }
 break;

The window identity is first obtained and the appropriate member of the array data->color_array is adjusted according to the command that is issued by the scroll bar. Note that the notifications scrollbar_notify::top and scrollbar_notify::bottom are only able to be generated by the keyboard (Home and end keys). When the scroll bar has the focus, it flashes the slider. The scroll bar position is set and then the newly formed numeric value is converted to a string so that it may be set into the appropriate display field. Based upon the window identity of the scroll bar, one of the display fields is updated with the textual equivalent of the new scroll value. The background brush of the client is then updated and the right-hand portion of the client is invalidated - causing its background to be painted in the new brush.

The message message::color_scrollbar is used to choose the color of the scroll bar as shown below.

    case message::color_scrollbar:
    {
        window_data* data = (window_data*)get_window_pointer(window_handle, 0);
        int i = get_window_integer((handle)parameter2, offset::identity);
        return (result)data->brush_array[i];
    }

The array brush_array is loaded with one of the brushes red, green or blue, depending upon the index. Similarly, the colors of the static display controls are adjusted using the notification message::color_display - which is shown below.

    case message::color_display:
    {
        window_data* data = (window_data*)get_window_pointer(window_handle, 0);

        int i = get_window_integer((handle)parameter2, offset::identity);

        if (i >= 3 && i <= 8)    // static text controls
        {
            set_text_color((handle)parameter1, data->primary_colors[i % 3]);
            set_background_color((handle)parameter1, get_system_color(system_color::button_highlight));
            return (result)data->display_brush;
        }
    }
    break;

Again the window identity is queried to obtain the appropriate index for the array of primary colors. The device context handle is passed as parameter 2 of the message. The text color and background color of this device context are adjusted prior to the control being drawn.

Backing up a little, the main routine creates all 10 child windows (7 displays and 3 scroll bars) - as shown below.

WindowRectangle = create_window(L"Static",
                               style::Child | style::Visible | display_style::RectangleWhite,
                               9,
                               window_handle);

character* color_labels[] = { "red", "green", "blue" };

for (int i=0; i<3; i++)
 {
  data->scroll_array[i] = create_window(L"Scrollbar",
                                 style::Child | style::Visible | style::Tabstop | scrollbar_style::Vertical,
                                 i,
                                 window_handle);

  data->label_array[i] = create_window(L"Static",
                               style::Child | style::Visible | display_style::Center,
                               i+3,
                               window_handle);

  set_window_text(data->label_array[i],color_labels[i]);

  data->value_array[i] = create_window(L"Static",
                               style::Child | style::Visible | display_style::Center,
                               i+6,
                               window_handle);

  set_window_text(data->value_array[i],"0");

  data->scroll_0rocedures[i] = (procedure) set_window_pointer(data->scroll_array[i],
                                                     offset::window_procedure,
                                                     scroll_procedure);

  set_scroll_range(data->scroll_array[i],scrollbar_identity::control,0,255,false);
  set_scroll_position(data->scroll_array[i],scrollbar_identity::control,0,false);
 }

As may be observed, 10 windows are created - three of which are subclassed.

Subclassing

In order to obtain a keyboard interface for switching amongst the scroll bars, the scroll bar window procedure is subclassed. This is achieved via the statement:

data->scroll_procedures[i] = (procedure) set_window_pointer(data->scroll_array[i],
                                                   offset::window_procedure,
                                                   scroll_procedure);

where a new window procedure is supplied for the scroll bars. That window procedure is shown below.

result __stdcall scroll_procedure(handle window_handle,
    unsigned identity,
    parameter parameter1,
    parameter parameter2)
{
    int i = get_window_integer(window_handle, offset::identity);

    window_data* data = (window_data*)get_window_pointer(get_parent(window_handle), 0);

    switch (identity)
    {
    case message::key_down:
        if ((int)parameter1 == virtual_key::tab)
            set_focus(data->scroll_array[(i + (get_key_state(virtual_key::shift) < 0 ? 2 : 1)) % 3]);
        break;

    case message::set_focus:
        data->focus_index = i;
        break;

    default: break;
    }

    return call_window_procedure(data->scroll_procedures[i],
        window_handle,
        identity,
        parameter1,
        parameter2);

The tab and backtab keys are intercepted and the new scrollbar to receive the focus is selected. all other messages are passed to the standard scroll bar window procedure (that is, the window procedure that was subclassed) via the function call_window_procedure.