Thread: ScrollWindow()

  1. #1
    Registered User
    Join Date
    Aug 2003
    Posts
    34

    ScrollWindow()

    Hi,

    I have a few questions about this scroll bar example from Charles Petzold's "Programming Windows." The code is at the bottom of the post.

    1) Why there's no UpdateWindow(hwnd) in the horizontal scrollbar message handler when there's one in vertical scrollbar message handler?

    2) Can you explain in more detail how ScrollWindow() works? I've read MSDN's explanation but it doesn't help much.

    It scrolls the client area, but invalidates the parts that are uncovered and they then need to be drawn on screen, right? This parts can only be located on the top or or on the bottom of the client area? (Since this is where uncovered parts appear)

    Code:
    LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
         static int  cxChar, cxCaps, cyChar, cxClient, cyClient, iMaxWidth ;
         HDC         hdc ;
         int         i, x, y, iVertPos, iHorzPos, iPaintBeg, iPaintEnd ;
         PAINTSTRUCT ps ;
         SCROLLINFO  si ;
         TCHAR       szBuffer[10] ;
         TEXTMETRIC  tm ;
         
         switch (message)
         {
         case WM_CREATE:
              hdc = GetDC (hwnd) ;
              
              GetTextMetrics (hdc, &tm) ;
              cxChar = tm.tmAveCharWidth ;
              cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2 ;
              cyChar = tm.tmHeight + tm.tmExternalLeading ;
              
              ReleaseDC (hwnd, hdc) ;
    
                   // Save the width of the three columns
              
              iMaxWidth = 40 * cxChar + 22 * cxCaps ;
              return 0 ;
              
         case WM_SIZE:
              cxClient = LOWORD (lParam) ;
              cyClient = HIWORD (lParam) ;
    
                   // Set vertical scroll bar range and page size
    
              si.cbSize = sizeof (si) ;
              si.fMask  = SIF_RANGE | SIF_PAGE ;
              si.nMin   = 0 ;
              si.nMax   = NUMLINES - 1 ;
              si.nPage  = cyClient / cyChar ;
              SetScrollInfo (hwnd, SB_VERT, &si, TRUE) ;
    
                   // Set horizontal scroll bar range and page size
    
              si.cbSize = sizeof (si) ;
              si.fMask  = SIF_RANGE | SIF_PAGE ;
              si.nMin   = 0 ;
              si.nMax   = 2 + iMaxWidth / cxChar ;
              si.nPage  = cxClient / cxChar ;
              SetScrollInfo (hwnd, SB_HORZ, &si, TRUE) ;
              return 0 ;
              
         case WM_VSCROLL:
                   // Get all the vertical scroll bar information
    
              si.cbSize = sizeof (si) ;
              si.fMask  = SIF_ALL ;
              GetScrollInfo (hwnd, SB_VERT, &si) ;
    
                   // Save the position for comparison later on
    
              iVertPos = si.nPos ;
    
              switch (LOWORD (wParam))
              {
              case SB_TOP:
                   si.nPos = si.nMin ;
                   break ;
                   
              case SB_BOTTOM:
                   si.nPos = si.nMax ;
                   break ;
                   
              case SB_LINEUP:
                   si.nPos -= 1 ;
                   break ;
                   
              case SB_LINEDOWN:
                   si.nPos += 1 ;
                   break ;
                   
              case SB_PAGEUP:
                   si.nPos -= si.nPage ;
                   break ;
                   
              case SB_PAGEDOWN:
                   si.nPos += si.nPage ;
                   break ;
                   
              case SB_THUMBTRACK:
                   si.nPos = si.nTrackPos ;
                   break ;
                   
              default:
                   break ;         
              }
                   // Set the position and then retrieve it.  Due to adjustments
                   //   by Windows it may not be the same as the value set.
    
              si.fMask = SIF_POS ;
              SetScrollInfo (hwnd, SB_VERT, &si, TRUE) ;
              GetScrollInfo (hwnd, SB_VERT, &si) ;
    
                   // If the position has changed, scroll the window and update it
    
              if (si.nPos != iVertPos)
              {                    
                   ScrollWindow (hwnd, 0, cyChar * (iVertPos - si.nPos), 
                                       NULL, NULL) ;
                   UpdateWindow (hwnd) ;
              }
              return 0 ;
              
         case WM_HSCROLL:
                   // Get all the horizontal scroll bar information
    
              //GetScrollInfo(hwnd, SB_HORZ, &si);
    
              si.cbSize = sizeof (si) ;
              si.fMask  = SIF_ALL ;
              GetScrollInfo (hwnd, SB_HORZ, &si) ;
    
                   // Save the position for comparison later on
    
             
              iHorzPos = si.nPos ;
    
              switch (LOWORD (wParam))
              {
              case SB_LINELEFT:
                   si.nPos -= 1 ;
                   break ;
                   
              case SB_LINERIGHT:
                   si.nPos += 1 ;
                   break ;
                   
              case SB_PAGELEFT:
                   si.nPos -= si.nPage ;
                   break ;
                   
              case SB_PAGERIGHT:
                   si.nPos += si.nPage ;
                   break ;
                   
              case SB_THUMBPOSITION:
                   si.nPos = si.nTrackPos ;
                   break ;
                   
              default :
                   break ;
              }
                   // Set the position and then retrieve it.  Due to adjustments
                   //   by Windows it may not be the same as the value set.
    
              si.fMask = SIF_POS ;
              SetScrollInfo (hwnd, SB_HORZ, &si, TRUE) ;
              GetScrollInfo (hwnd, SB_HORZ, &si) ;
              
                   // If the position has changed, scroll the window 
    
              if (si.nPos != iHorzPos)
              {
                   ScrollWindow (hwnd, cxChar * (iHorzPos - si.nPos), 0, 
                                 NULL, NULL) ;
                   //UpdateWindow(hwnd);
              }
              return 0 ;
    
         case WM_PAINT :
              hdc = BeginPaint (hwnd, &ps) ;
    
                   // Get vertical scroll bar position
    
              si.cbSize = sizeof (si) ;
              si.fMask  = SIF_POS ;
              GetScrollInfo (hwnd, SB_VERT, &si) ;
              iVertPos = si.nPos ;
    
                   // Get horizontal scroll bar position
    
              GetScrollInfo (hwnd, SB_HORZ, &si) ;
              iHorzPos = si.nPos ;
    
                   // Find painting limits
    
              iPaintBeg = max (0, iVertPos + ps.rcPaint.top / cyChar) ;
              iPaintEnd = min (NUMLINES - 1,
                               iVertPos + ps.rcPaint.bottom / cyChar) ;
              
              for (i = iPaintBeg ; i <= iPaintEnd ; i++)
              {
                   x = cxChar * (1 - iHorzPos) ;
                   y = cyChar * (i - iVertPos) ;
                   
                   TextOut (hdc, x, y,
                            sysmetrics[i].szLabel,
                            lstrlen (sysmetrics[i].szLabel)) ;
                   
                   TextOut (hdc, x + 22 * cxCaps, y,
                            sysmetrics[i].szDesc,
                            lstrlen (sysmetrics[i].szDesc)) ;
                   
                   SetTextAlign (hdc, TA_RIGHT | TA_TOP) ;
                   
                   TextOut (hdc, x + 22 * cxCaps + 40 * cxChar, y, szBuffer,
                            wsprintf (szBuffer, TEXT ("%5d"),
                                 GetSystemMetrics (sysmetrics[i].iIndex))) ;
                   
                   SetTextAlign (hdc, TA_LEFT | TA_TOP) ;
              }
    
              EndPaint (hwnd, &ps) ;
              return 0 ;
              
         case WM_DESTROY :
              PostQuitMessage (0) ;
              return 0 ;
         }
         return DefWindowProc (hwnd, message, wParam, lParam) ;
    }

  2. #2
    jasondoucette.com JasonD's Avatar
    Join Date
    Mar 2003
    Posts
    278
    You are right that Petzold's code uses UpdateWindow() in one, and not the other. However, this is not really a bug, although I am not sure why he did not choose to use it in both places, or neither. From the MSDN page for ScrollWindow(), it states:

    "The area uncovered by ScrollWindow is not repainted, but it is combined into the window's update region. The application eventually receives a WM_PAINT message notifying it that the region must be repainted. To repaint the uncovered area at the same time the scrolling is in action, call the UpdateWindow function immediately after calling ScrollWindow."

    If you'll look at the MSDN page for UpdateWindow(), it states:

    "The UpdateWindow function updates the client area of the specified window by sending a WM_PAINT message to the window if the window's update region is not empty. The function sends a WM_PAINT message directly to the window procedure of the specified window, bypassing the application queue. If the update region is empty, no message is sent."

    So, MSDN is pretty good at explaining what is going on.

    If you wish to process a WM_PAINT message immediately, use UpdateWindow(). WM_PAINT is a low priority message, so it does not get processed until almost all other messages are processed. In this case, if the CPU is doing a lot, the window will appear to be painted in two steps - ScrollWindow() scrolls the window, and at a later time WM_PAINT redraws the invalid portion that is missing. Since UpdateWindow() forces any invalid portions to be drawn immediately, you can use this to fix this problem. I would call it every time I use ScrollWindow().

  3. #3
    Registered User
    Join Date
    Aug 2003
    Posts
    34
    OK, thanks. Can you explain this part for me?

    Code:
    x = cxChar * (1 - iHorzPos) ;
    Why the 1-iHorzPos?

  4. #4
    jasondoucette.com JasonD's Avatar
    Join Date
    Mar 2003
    Posts
    278
    Ok, but first, explain to me what the three variables stand for (i.e. what do their values mean, and what are they used for?). I have a feeling you'll answer your own question if you do this.

  5. #5
    jasondoucette.com JasonD's Avatar
    Join Date
    Mar 2003
    Posts
    278
    Originally posted by Ariod
    1) Why there's no UpdateWindow(hwnd) in the horizontal scrollbar message handler when there's one in vertical scrollbar message handler?
    Originally posted by JasonD
    ...although I am not sure why he did not choose to use it in both places, or neither.
    I looked at all of Petzold's examples that use ScrollWindow(), and I know why he chose to use it for vertical scrolling, and not horizontal scrolling. You'll notice that he chooses to process SB_THUMBTRACK for vertical scrolling (which updates the window as the user moves the mouse), and only processes SB_THUMBPOSITION for the horizontal scroll (which updates the window after the user stops moving the mouse). Shrink the window so that both scroll bars appear, and you'll see what I mean. You'll see that it is not so important to force a window update after the scroll when it is only drawn once. However, for a scroll that continually updates the window, the WM_PAINT messages are pushed to the back of the priority queue, and you could have a situation where the window continues to scroll, but the missing portion is not painted until you let go of the mouse.

    Of course, this begs the question: Why not process SB_THUMBTRACK for both scroll bars? I had noticed he did not do this before, but I had not included it in my errata page, since it was not really a bug. However, there is no reason to have one scroll bar update as the mouse moves, and the other one not (even if it is the scroll bar that is unlikely to be used). Also, because this makes readers wonder why UpdateWindow() isn't used in both places, I have decided to add these errata to my page.

    I hope this helps explain his code.

  6. #6
    Registered User
    Join Date
    Aug 2003
    Posts
    34
    Originally posted by JasonD
    Ok, but first, explain to me what the three variables stand for (i.e. what do their values mean, and what are they used for?). I have a feeling you'll answer your own question if you do this.
    x - coordinates
    cxChar - average character width
    iHorzPos - position of the horizontal scroll bar

    If client area is scrolled to the left, part of text is being drawn in the negative area of the x axis, outside the client area?

    But why 1 - iHorzPos, why not just -iHorzPos? To avoid negative value when iHorzPos is 0?

  7. #7
    jasondoucette.com JasonD's Avatar
    Join Date
    Mar 2003
    Posts
    278
    It's the way it is to insert a space to the left of the first column of text. Try removing the 1, and you'll see how it differs. From Petzold's code, he has specified that there should be 1 space to the left of the first column (with this statement you are inquiring about), and 2 spaces to the right of the last column (inside the WM_SIZE message handler, where the scroll bar's range is set). This is purely personal taste, but you usually do not want your text flush against the client area's edge.

  8. #8
    Registered User
    Join Date
    Aug 2003
    Posts
    34

    Post

    Thanks again Jason

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. What is ScrollWindow() ?
    By Newbeee in forum Windows Programming
    Replies: 1
    Last Post: 07-04-2006, 06:47 PM