Thread: C win32 API - Scrollbar won't work

  1. #1
    Registered User
    Join Date
    Aug 2006
    Location
    England
    Posts
    5

    Question C win32 API - Scrollbar won't work

    Hi guys, sorry my first post here is a question with a load of code...I'll pay you guys back .

    So, here is my question...I have been reading Petzold's book (Programming Windows - Fifth Edition) and I am up to the scrollbar chapter. So I thought I would try my own version, everything comes up fine, but when I press the down arrow on the window - it doesn't scroll down on the client area, can anybody see the problem?

    I have been through every line of this code many times and I can't find the problem, posting here is a last resort.

    Code:
    #include <windows.h>
    #include <stdio.h>
    
    /* number of lines to print */
    #define LINES 30
    
    /*  Declare Windows procedure  */
    LRESULT CALLBACK WindowProcedure(HWND, UINT, WPARAM, LPARAM);
    
    /*  Make the class name into a global variable  */
    char szClassName[ ] = "WindowsApp";
    
    int WINAPI WinMain(HINSTANCE hThisInstance,
                       HINSTANCE hPrevInstance,
                       LPSTR lpszArgument,
                       int nFunsterStil)
    
    {
        HWND hwnd;               /* This is the handle for our window */
        MSG messages;            /* Here messages to the application are saved */
        WNDCLASSEX wincl;        /* Data structure for the windowclass */
    
        /* The Window structure */
        wincl.hInstance = hThisInstance;
        wincl.lpszClassName = szClassName;
        wincl.lpfnWndProc = WindowProcedure;      /* This function is called by windows */
        wincl.style = CS_HREDRAW | CS_VREDRAW ;
        wincl.cbSize = sizeof(WNDCLASSEX);
        wincl.hIcon = LoadIcon(NULL, IDI_APPLICATION);
        wincl.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
        wincl.hCursor = LoadCursor(NULL, IDC_ARROW);
        wincl.lpszMenuName = NULL;
        wincl.cbClsExtra = 0;
        wincl.cbWndExtra = 0;
        wincl.hbrBackground = (HBRUSH) GetStockObject(BLACK_BRUSH);
    
        /* Register the window class, and if it fails quit the program */
        if (!RegisterClassEx(&wincl)) return(0);
    
        /* The class is registered, let's create the program*/
        hwnd = CreateWindowEx(
               0,
               szClassName,
               "Matt's Scrollbar Window",
               WS_OVERLAPPEDWINDOW | WS_VSCROLL, /* default + scrollbar window */
               CW_USEDEFAULT,                    /* Windows decides the position */
               CW_USEDEFAULT,                    /* where the window ends up on the screen */
               500,                              /* The programs width */
               350,                              /* and height in pixels */
               NULL,
               NULL,
               hThisInstance,
               NULL);
    
        /* Make the window visible on the screen */
        ShowWindow(hwnd, nFunsterStil);
    
        /* Run the message loop. It will run until GetMessage() returns 0 */
        while (GetMessage(&messages, NULL, 0, 0))
        {
            TranslateMessage(&messages);
            DispatchMessage(&messages);
        }
    
        /* The program return-value is 0 - The value that PostQuitMessage() gave */
        return(messages.wParam);
    }
    
    LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
        static int  cxChar, cxCaps, cyChar, cyClient, VscrollPos;
        int i;
        PAINTSTRUCT ps;
        char buffer[15];
        TEXTMETRIC tm;
        
        switch (message)                  /* handle the messages */
        {
            case WM_DESTROY:
                PostQuitMessage(0);       /* send a WM_QUIT to the message queue */
                break;
            case WM_CREATE:
                {
                HDC hdc = GetDC(hwnd);
    
                /* Get the sizes of the system font, for formatting */
                GetTextMetrics(hdc, &tm);
                cxChar = tm.tmAveCharWidth;
                cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2;
                cyChar = tm.tmHeight + tm.tmExternalLeading;
    
                ReleaseDC(hwnd, hdc);
                
                SetScrollRange(hwnd, SB_VERT, 0, LINES - 1, FALSE);
                SetScrollPos(hwnd, SB_VERT, VscrollPos, TRUE);
                
                }
            case WM_PAINT:
                {
                PAINTSTRUCT ps;
                HDC hdc = BeginPaint(hwnd, &ps);
                SetTextColor(hdc, RGB(0, 255, 0));      /* set text colour to green */
                SetBkColor(hdc, RGB(0, 0, 0));          /* set background colour to black */
                /* display the lines of text */
                for (i = 0; i < LINES; i++) {
                    sprintf(buffer, "Line number: %d", i);
                    TextOut(hdc, 0, cyChar * i, buffer, strlen(buffer));
                }
                EndPaint(hwnd, &ps); 
                break;
                }
            /* taken from 'Programming Windows - Fifth Edition' */
            case WM_VSCROLL:
                {
                switch (LOWORD(wParam))
                {
                case SB_LINEUP:
                     VscrollPos -= 1;
                     break;
                case SB_LINEDOWN:
                     VscrollPos += 1;
                     break;
                case SB_PAGEUP:
                     VscrollPos -= cyClient / cyChar;
                     break;
                case SB_PAGEDOWN:
                     VscrollPos += cyClient / cyChar;
                     break;
                case SB_THUMBPOSITION:
                     VscrollPos = HIWORD(wParam);
                     break;
                default:
                     break;
                }
    
                VscrollPos = max(0, min(VscrollPos, LINES - 1));
    
                if (VscrollPos != GetScrollPos(hwnd, SB_VERT)) {
                    SetScrollPos(hwnd, SB_VERT, VscrollPos, TRUE);
                    InvalidateRect(hwnd, NULL, TRUE);
                }
                break;
                }
            default:
                return DefWindowProc(hwnd, message, wParam, lParam);
        }
    
        return(0);
    }
    Any help would be much appreciated, I'd like to move onto the next chapter .
    Cheers,
    Matt. Stevens

  2. #2
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    Scrolling in Win32 is not automatic. You have to make your paint code display the appropriate thing based on the scroll position. For your program, this is pretty simple, we can just make a minor change to the code painting the lines:
    Code:
                /* i is the actual line number, j is the line position.
                 * eg. if the scroll positon is 7, then we start with line 7 at position 0 */
                for (i = GetScrollPos(hwnd, SB_VERT), j = 0; i < LINES; i++, j++)
                {
                    sprintf(buffer, "Line number: %d", i);
                    TextOut(hdc, 0, cyChar * j, buffer, strlen(buffer));
                }
    As a rule, you need to start with the content that belongs at the top with the current scroll position. For example, if the scroll position is 7 lines, then you should start with line 7 at the top of the screen.

    There is another simpler, although less efficient, method. This uses the OffsetWindowOrgEx function. This function changes the coordinate system by the specified amount. So for example, if we give it the value 100, it will make the position 0 map to the actual positon -100, 100 map to the actual postion 0, and so on. Therefore, by placing a call to this function before we start painting we can cause a scroll without changing our painting code as all the content will automatically be moved up by the specified amount. For your code, for example:
    Code:
                OffsetWindowOrgEx(hdc, 0, GetScrollPos(hwnd, SB_VERT) * cyChar, NULL);
    
                for (i = 0; i < LINES; i++) {
                    sprintf(buffer, "Line number: %d", i);
                    TextOut(hdc, 0, cyChar * i, buffer, strlen(buffer));
                }
    This can be inneficient because we are painting items that won't be seen. This is not a problem for thirty lines but would be for 100,000.

    A couple of other things:
    - buffer is not large enough, causing a crash on my machine.
    - Define variable locally if possible. For example, buffer should be defined just above the sprintf call; i and j should be defined in the WM_PAINT block, etc.
    - SB_PAGEUP and SB_PAGEDOWN (clicking on the scroll bar do not work) because cyClient is not initialised.
    - Ideally, we shouldn't paint lines that are below the bottom of the window. Using cyChar * j and cyClient (after it has been initialised), you can test if the current position is below the bottom of the window and break out of the loop.
    Last edited by anonytmouse; 08-03-2006 at 08:08 PM.

  3. #3
    Registered User
    Join Date
    Aug 2006
    Location
    England
    Posts
    5

    Talking

    wow, thank you so much. I wasn't expecting a reply that fast, especially one as informative and easy to understand as yours. I initialised cyClient in WM_SIZE and from now on I will not print below the window. Worked great first time .

    Thanks again

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Win32 API and MFC
    By loopshot in forum C++ Programming
    Replies: 6
    Last Post: 12-07-2004, 11:58 AM
  2. Win32 API & XML question
    By Born_2B_Alone in forum Windows Programming
    Replies: 8
    Last Post: 09-23-2004, 04:32 PM
  3. Classes and Win32 API, and another Question
    By philvaira in forum Windows Programming
    Replies: 10
    Last Post: 04-10-2004, 07:21 PM
  4. FILES in WinAPI
    By Garfield in forum Windows Programming
    Replies: 46
    Last Post: 10-02-2003, 06:51 PM
  5. Allegro and the Win32 API
    By -leech- in forum Game Programming
    Replies: 1
    Last Post: 02-12-2002, 09:08 PM