Page 1 of 1

Microsoft : Working with Scrollbars How to process window scrollbars Rate Topic: -----

#1 Martyn.Rae  Icon User is offline

  • The programming dinosaur
  • member icon

Reputation: 540
  • View blog
  • Posts: 1,406
  • Joined: 22-August 09

Posted 19 March 2010 - 09:38 AM

Microsoft : Working with Scrollbars

Introduction

There are often times when it is necessary to display windows with scroll bars, in order that the user have the ability to move up and down large volumes of data. We are all aware of scroll bars, so let's jump in feet first with our base window code modified to display a vertical scrollbar.

#include <windows.h>
#include <ctime>
#include <cstdlib>
using namespace std;

class frame_window {
    private:    LPWSTR window_class_name;
                HINSTANCE instance_handle;
                HWND window_handle;
                HWND scroll_handle;
                RECT client_rectangle; 
    public:     frame_window(LPWSTR window_class_identity) : window_class_name(window_class_identity) { 
                    int screen_width = GetSystemMetrics(SM_CXFULLSCREEN);
                    int screen_height = GetSystemMetrics(SM_CYFULLSCREEN);
                    instance_handle = GetModuleHandle(NULL);
                    HCURSOR cursor_arrow = LoadCursor(instance_handle, IDC_ARROW); 
                    WNDCLASS window_class = { CS_OWNDC, main_window_proc, 0, 0,  
                                              instance_handle, NULL,  
                                              cursor_arrow,  
                                              NULL, NULL,  
                                              window_class_name }; 
                 
                    //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
                    // Create a standard frame window 
                    //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
                 
                    RegisterClass(&window_class); 
                    window_handle = CreateWindowEx(WS_EX_WINDOWEDGE|WS_EX_CLIENTEDGE,  
                                                   window_class_name,  
                                                   L"My Base Window", 
                                                   WS_OVERLAPPEDWINDOW,
                                                   100, 100, screen_width-200,  
                                                   screen_height-200, NULL, NULL, 
                                                   instance_handle, NULL); 
                    RECT rect;
                    GetClientRect(window_handle, &rect);
                    scroll_handle = CreateWindowEx(0,  
                                                   L"ScrollBar",  
                                                   L"", 
                                                   WS_CHILD|WS_VISIBLE|SBS_VERT, rect.right-rect.left-15, 
                                                   0, 16,  
                                                   rect.bottom-rect.top+1, window_handle, NULL, 
                                                   instance_handle, NULL); 
                    SetWindowLongPtr(window_handle, GWL_USERDATA, (LONG)this);
                }
                ~frame_window() {
                    UnregisterClass(window_class_name, instance_handle); 
                }
                //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
                // Windows message processing function 
                //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
                static LRESULT WINAPI main_window_proc(HWND window_handle, UINT message,  
                                                       WPARAM wparam, LPARAM lparam) {
                    frame_window *This = (frame_window *)GetWindowLongPtr(window_handle, GWL_USERDATA);
                    switch ( message ) { 
                        case WM_PAINT: 
                        { 
                            PAINTSTRUCT paint_structure; 
                            RECT client_rect; 
                            HDC paint_device_context, paint_dc; 
                            HBITMAP bitmap; 

                            paint_device_context = BeginPaint(window_handle, &paint_structure); 
                            paint_dc = CreateCompatibleDC(paint_device_context); 
                            GetClientRect(window_handle, &client_rect); 
                            int window_width = client_rect.right - client_rect.left;
                            int window_height = client_rect.bottom - client_rect.top;
                            bitmap = CreateBitmap(window_width, window_height, 1, 32, NULL);
                            HGDIOBJ old_bitmap = SelectObject(paint_dc, bitmap);

                            // Fill the client aread with the user selected face colour
                            HBRUSH fill_brush = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
                            FillRect(paint_dc, &paint_structure.rcPaint, fill_brush);
                            DeleteObject(fill_brush);
                                                       
                            BitBlt(paint_device_context, 0, 0,  
                                   client_rect.right-client_rect.left, 
                                   client_rect.bottom-client_rect.top, 
                                   paint_dc, 0, 0, SRCCOPY); 
                            SelectObject(paint_dc, old_bitmap); 
                            DeleteObject(bitmap); 
                            DeleteDC(paint_dc); 
                            EndPaint(window_handle, &paint_structure); 
                            return 0; 
                        } 
                        case WM_ERASEBKGND: 
                        { 
                            return TRUE; 
                        } 
                        case WM_SIZE: 
                        { 
                            InvalidateRect(window_handle, NULL, TRUE); 
                            return 0; 
                        } 
                        case WM_CLOSE: 
                        { 
                            //++++++++++++++++++++++++++++++++++++++++++++++++++++ 
                            // The user wants to close the window 
                            //++++++++++++++++++++++++++++++++++++++++++++++++++++ 
                 
                            PostQuitMessage(0); 
                            return 0; 
                        } 
                    } 
                    return DefWindowProc(window_handle, message, wparam, lparam); 
                } 
                
                //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
                // Windows message loop 
                //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
                void run() {
                    MSG window_message; 
                    while ( GetMessage(&window_message, NULL, 0, 0) ) { 
                        TranslateMessage(&window_message); 
                        DispatchMessage(&window_message); 
                    } 
                }
};
 
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
// Windows main entry point 
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
 
int WINAPI wWinMain(HINSTANCE instance_handle, HINSTANCE, LPWSTR, INT) {
    frame_window mainWindow(L"my base window");
    mainwindow.run();
    return 0; 
}



Now as it stands, there is very little that happens, as we have nothing to scroll, and we have'nt set up the scroll bars anyway.

The Vertical Scroll Bar

Let's display a grid with numbers in the area so we can see them scroll. The code for that follows:

#include <windows.h>
#include <ctime>
#include <cstdlib>
using namespace std;

class frame_window {
    private:    LPWSTR window_class_name;
                HINSTANCE instance_handle;
                HWND window_handle;
                HWND scroll_handle;
                RECT client_rectangle;
                int line_number;
    public:     frame_window(LPWSTR window_class_identity) : window_class_name(window_class_identity),
                                                             instance_handle(NULL),
                                                             line_number(1) { 
                    int screen_width = GetSystemMetrics(SM_CXFULLSCREEN);
                    int screen_height = GetSystemMetrics(SM_CYFULLSCREEN);
                    instance_handle = GetModuleHandle(NULL);
                    HCURSOR cursor_arrow = LoadCursor(instance_handle, IDC_ARROW); 
                    WNDCLASS window_class = { CS_OWNDC, main_window_proc, 0, 0,  
                                              instance_handle, NULL,  
                                              cursor_arrow,  
                                              NULL, NULL,  
                                              window_class_name }; 
                 
                    //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
                    // Create a standard frame window 
                    //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
                 
                    RegisterClass(&window_class); 
                    window_handle = CreateWindowEx(WS_EX_WINDOWEDGE|WS_EX_CLIENTEDGE,  
                                                   window_class_name,  
                                                   L"My Base Window", 
                                                   WS_OVERLAPPEDWINDOW|WS_CLIPCHILDREN, 100, 
                                                   100, screen_width-200,  
                                                   screen_height-200, NULL, NULL, 
                                                   instance_handle, NULL);
                    RECT rect;
                    GetClientRect(window_handle, &rect);
                    scroll_handle = CreateWindowEx(0,  
                                                   L"ScrollBar",  
                                                   L"", 
                                                   WS_CHILD|WS_VISIBLE|SBS_VERT, rect.right-rect.left-15, 
                                                   0, 16,  
                                                   rect.bottom-rect.top+1, window_handle, NULL, 
                                                   instance_handle, NULL); 
                    SetWindowLongPtr(window_handle, GWL_USERDATA, (LONG)this);
                    ShowWindow(window_handle, SW_SHOW); 
                    UpdateWindow(window_handle);
                }
                ~frame_window() {
                    UnregisterClass(window_class_name, instance_handle); 
                }
                //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
                // Windows message processing function 
                //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
                static LRESULT WINAPI main_window_proc(HWND window_handle, UINT message,  
                                                       WPARAM wparam, LPARAM lparam) {
                    frame_window *This = (frame_window *)GetWindowLongPtr(window_handle, GWL_USERDATA);
                    switch ( message ) { 
                        case WM_PAINT: 
                        { 
                            PAINTSTRUCT paint_structure; 
                            RECT client_rect; 
                            HDC paint_device_context, paint_dc; 
                            HBITMAP bitmap; 

                            paint_device_context = BeginPaint(window_handle, &paint_structure); 
                            paint_dc = CreateCompatibleDC(paint_device_context); 
                            GetClientRect(window_handle, &client_rect); 
                            int window_width = client_rect.right - client_rect.left;
                            int window_height = client_rect.bottom - client_rect.top;
                            bitmap = CreateBitmap(window_width, window_height, 1, 32, NULL);
                            HGDIOBJ old_bitmap = SelectObject(paint_dc, bitmap);

                            // Fill the client aread with the user selected face colour
                            HBRUSH light_brush = CreateSolidBrush(RGB(255, 255, 238));
                            FillRect(paint_dc, &paint_structure.rcPaint, light_brush);
                            DeleteObject(light_brush);
                            
                            // Draw the hoizontal lines across the page and print draw the numbers
                            int ix, line_number;
                            for ( ix = 20, line_number = This->line_number; 
                                  ix < window_height+20; 
                                  ix+=20, line_number++ ) {
                                if ( line_number <= 100 ) {
                                    wchar_t line_number_text[10];
                                    MoveToEx(paint_dc, 0, ix, NULL);
                                    LineTo(paint_dc, window_width, ix);
                                    wsprintf(line_number_text, L"%d", line_number);
                                    RECT text_rectangle = { 5, ix-18, window_width-10, ix-2 };
                                    DrawText(paint_dc, line_number_text, wcslen(line_number_text),
                                             &text_rectangle, DT_LEFT);
                                }
                            }
                                                       
                            BitBlt(paint_device_context, 0, 0,  
                                   client_rect.right-client_rect.left, 
                                   client_rect.bottom-client_rect.top, 
                                   paint_dc, 0, 0, SRCCOPY); 
                            SelectObject(paint_dc, old_bitmap); 
                            DeleteObject(bitmap); 
                            DeleteDC(paint_dc); 
                            EndPaint(window_handle, &paint_structure); 
                            return 0; 
                        } 
                        case WM_ERASEBKGND: 
                        { 
                            return TRUE; 
                        } 
                        case WM_SIZE: 
                        { 
                            // Set up the vertical scroll bar
                            int items = HIWORD(lparam)/20;
                            SCROLLINFO scrollInfo;
                            scrollInfo.cbSize = sizeof(SCROLLINFO);
                            scrollInfo.fMask = SIF_POS | SIF_RANGE | SIF_PAGE;
                            scrollInfo.nMax = 100;
                            scrollInfo.nMin = 1;
                            scrollInfo.nPage = items;
                            scrollInfo.nPos = 1;
                            scrollInfo.nTrackPos = 0;
                            SetScrollInfo(This->scroll_handle, SB_CTL, &scrollInfo, TRUE);
                            SetWindowPos(This->scroll_handle, NULL, LOWORD(lparam)-15, 0, 16, 
                                         HIWORD(lparam), SWP_NOZORDER);
                            RECT window_rect;
                            GetWindowRect(window_handle, &window_rect);
                            window_rect.bottom -=  (HIWORD(lparam)%20);
                            MoveWindow(window_handle,
                                       window_rect.left, 
                                       window_rect.top,
                                       window_rect.right-window_rect.left,
                                       window_rect.bottom-window_rect.top,
                                       TRUE);
                            InvalidateRect(window_handle, NULL, TRUE); 
                            return 0; 
                        } 
                        case WM_VSCROLL:
                        {
                            switch ( LOWORD(wparam) ) {
                                case SB_LINEUP:
                                {
                                    if ( --This->line_number == 0 ) This->line_number = 1;
                                    break;
                                }
                                case SB_LINEDOWN:
                                {
                                    RECT client_rect;
                                    GetClientRect(window_handle, &client_rect);
                                    int items = (client_rect.bottom-client_rect.top)/20;
                                    ++This->line_number;
                                    if (  This->line_number > (100-items) + 1 ) This->line_number = 100-items+1;
                                    break;
                                }
                                case SB_PAGEUP:
                                {
                                    RECT client_rect;
                                    GetClientRect(window_handle, &client_rect);
                                    int items = (client_rect.bottom-client_rect.top)/20;
                                    This->line_number -= items;
                                    if (  This->line_number < 1 ) This->line_number = 1;
                                    break;
                                }
                                case SB_PAGEDOWN:
                                {
                                    RECT client_rect;
                                    GetClientRect(window_handle, &client_rect);
                                    int items = (client_rect.bottom-client_rect.top)/20;
                                    This->line_number += items;
                                    if (  This->line_number > (100-items) + 1 ) This->line_number = (100-items)+1;
                                    break;
                                }
                                case SB_THUMBTRACK:
                                {
                                    This->line_number = HIWORD(wparam);
                                    break;
                                }
                                case SB_THUMBPOSITION:
                                {
                                    This->line_number = HIWORD(wparam);
                                    break;
                                }
                            }
                            SCROLLINFO scrollInfo;
                            scrollInfo.cbSize = sizeof(SCROLLINFO);
                            scrollInfo.fMask = SIF_POS;
                            scrollInfo.nPos = This->line_number;
                            SetScrollInfo(This->scroll_handle, SB_CTL, &scrollInfo, TRUE);
                            InvalidateRect(window_handle, NULL, TRUE); 
                            return 0; 
                        }
                        case WM_CLOSE: 
                        { 
                            //++++++++++++++++++++++++++++++++++++++++++++++++++++ 
                            // The user wants to close the window 
                            //++++++++++++++++++++++++++++++++++++++++++++++++++++ 
                 
                            PostQuitMessage(0); 
                            return 0; 
                        } 
                    } 
                    return DefWindowProc(window_handle, message, wparam, lparam); 
                } 
                
                //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
                // Windows message loop 
                //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
                void run() {
                    MSG window_message; 
                    while ( GetMessage(&window_message, NULL, 0, 0) ) { 
                        TranslateMessage(&window_message); 
                        DispatchMessage(&window_message); 
                    } 
                }
};
 
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
// Windows main entry point 
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
 
int WINAPI wWinMain(HINSTANCE instance_handle, HINSTANCE, LPWSTR, INT) {
    frame_window mainWindow(L"my base window");
    mainwindow.run();
    return 0; 
}



Now, let's look at the points of interest in that code. We will ignore the extra code in the WM_PAINT because all it does is to draw lines and display the numbers on the line. What is interesting is the extra code in the WM_SIZE and WM_VSCROLL message handling.

The code that handles the WM_SIZE message, sets the scrollbar information and the size and position of the scrollbar as the window is resized by the user. The scrollbar informtion changes, because whilst the number of items being 'held' by the list remains at a constant 100, the actual number of items in the page changes. As the window's height increases and decreases, we need to see that reflected in the thumb. I have chosen to update the range and position as well, as the first WM_SIZE message is received during the creation operation and there they do need to be set as it's not done anywhere else.

The size and position of the scrollbar also needs to be adjusted as the window is resized, but I think that makes sense I hope. What you will notice however is that I have made the code such that on resizing, it 'snaps' to a whole number of lines.

The WM_VSCROLL message processes the user's interaction with the vertical scroll bar. We get notified when the user wants to scroll up, scroll down, page up, page down, when they are dragging the thumb and when they have released it.

The Horizontal Scroll Bar

This works in exactly the same way as the vertical scroll bar, except the message that is sent to the window procedure is WM_HSCROLL.

Conclusion

Scroll bars are a necessary evil, and are particularly problematic when you have to rearrange your real-estate to cater for them being displayed and removed as the data to be displayed increases or decreases.

Is This A Good Question/Topic? 2
  • +

Page 1 of 1