Example of Scrolling Text

The following example shows how to have your application scroll text in response to input from the horizontal and vertical scroll bars.

HDC hdc; 
PAINTSTRUCT ps; 
TEXTMETRIC tm; 
SCROLLINFO si; 
 
// These variables are required to display text. 
 
static int xClient;     // width of client area 
static int yClient;     // height of client area 
static int xClientMax;  // maximum width of client area 
 
static int xChar;       // horizontal scrolling unit 
static int yChar;       // vertical scrolling unit 
static int xUpper;      // average width of uppercase letters 
 
static int xPos;        // current horizontal scrolling position 
static int yPos;        // current vertical scrolling position 
 
static int xMax;        // maximum horizontal scrolling position 
static int yMax;        // maximum vertical scrolling position 
 
int xInc;               // horizontal scrolling increment 
int yInc;               // vertical scrolling increment 
 
int i;                  // loop counter 
int x, y;               // horizontal and vertical coordinates
 
int FirstLine;          // first line in the invalidated area 
int LastLine;           // last line in the invalidated area 
 
// Create an array of lines to display. 
 
#define LINES 27 
static char *abc[] = { "anteater", "bear", "cougar", "dingo", 
  "elephant", "frog", "gazelle", "hyena", "iguana", "jackal", 
  "kangaroo", "llama", "moose", "newt", "octopus", "penguin", 
  "quail", "rat", "squid", "tortoise", "urus", "vole", 
  "walrus", "xylophone", "yak", "zebra", 
  "This line contains many words, but no character. Go figure." }; 
 
switch (uMsg) 
{ 
    case WM_CREATE : 
 
        // Get the handle to the client area's device context. 
 
        hdc = GetDC (hwnd); 
 
        // Extract font dimensions from the text metrics. 
 
        GetTextMetrics (hdc, &tm); 
        xChar = tm.tmAveCharWidth; 
        xUpper = (tm.tmPitchAndFamily & 1 ? 3 : 2) * xChar/2; 
        yChar = tm.tmHeight + tm.tmExternalLeading; 
 
        // Free the device context. 
 
        ReleaseDC (hwnd, hdc); 
 
        // Set an arbitrary maximum width for client area. 
        // (xClientMax is the sum of the widths of 48 average 
        // lowercase letters and 12 uppercase letters.) 
 
        xClientMax = 48 * xChar + 12 * xUpper; 
 
        return 0; 
 
    case WM_SIZE: 
 
        // Retrieve the dimensions of the client area. 
 
        yClient = HIWORD (lParam); 
        xClient = LOWORD (lParam); 
 
        // Determine the maximum vertical scrolling position. 
        // The two is added for extra space below the lines 
        // of text. 
 
        yMax = max (0, LINES + 2 - yClient/yChar); 
 
        // Make sure the current vertical scrolling position 
        // does not exceed the maximum. 
 
        yPos = min (yPos, yMax); 
 
        // Adjust the vertical scrolling range and scroll box 
        // position to reflect the new yMax and yPos values. 
 
        si.cbSize = sizeof(si); 
        si.fMask  = SIF_RANGE | SIF_PAGE | SIF_POS; 
        si.nMin   = 0; 
        si.nMax   = yMax; 
        si.nPage  = yClient / yChar; 
        si.nPos   = yPos; 
        SetScrollInfo(hwnd, SB_VERT, &si, TRUE); 
 
        // Determine the maximum horizontal scrolling position. 
        // The two is added for extra space to the right of the 
        // lines of text. 
 
        xMax = max (0, 2 + (xClientMax - xClient)/xChar); 
 
        // Make sure the current horizontal scrolling position 
        // does not exceed the maximum. 
 
        xPos = min (xPos, xMax); 
 
        // Adjust the horizontal scrolling range and scroll box 
        // position to reflect the new xMax and xPos values. 
 
        si.cbSize = sizeof(si); 
        si.fMask  = SIF_RANGE | SIF_PAGE | SIF_POS; 
        si.nMin   = 0; 
        si.nMax   = xMax; 
        si.nPage  = xClient / xChar; 
        si.nPos   = xPos; 
        SetScrollInfo(hwnd, SB_HORZ, &si, TRUE); 
 
        return 0; 
 
    case WM_PAINT: 
 
        // Prepare the window for painting. 
 
        hdc = BeginPaint(hwnd, &ps); 
 
        // Use the current vertical scrolling position and 
        // coordinates of the invalid rectangle to determine 
        // the range of new lines that should be drawn in the 
        // client area. 
 
        FirstLine = max (0, yPos + ps.rcPaint.top/yChar - 1); 
        LastLine = min (LINES, yPos + ps.rcPaint.bottom/yChar); 
 
        // Display these lines. 
 
        for (i = FirstLine;i < LastLine;i++) 
        { 
            x = xChar * (1 - xPos); 
            y = yChar * (1 - yPos + i); 
 
            TextOut (hdc, x, y, abc[i], lstrlen(abc[i])); 
        } 
 
        // Indicate that painting is finished. 
 
        EndPaint(hwnd, &ps); 
        break; 
 
    case WM_HSCROLL: 
        switch(LOWORD (wParam)) 
        { 
            // User clicked shaft left of the scroll box. 
 
            case SB_PAGEUP: 
                 xInc = -8; 
                 break; 
 
            // User clicked shaft right of the scroll box. 
 
            case SB_PAGEDOWN: 
                 xInc = 8; 
                 break; 
 
            // User clicked the left arrow. 
 
            case SB_LINEUP: 
                 xInc = -1; 
                 break; 
 
            // User clicked the right arrow. 
 
            case SB_LINEDOWN: 
                 xInc = 1; 
                 break; 
 
            // User dragged the scroll box. 
 
            case SB_THUMBTRACK: 
                 xInc = HIWORD(wParam) - xPos; 
                 break; 
 
            default: 
                 xInc = 0; 
 
        } 
 
        // If applying the horizontal scrolling increment does not 
        // take the scrolling position out of the scrolling range, 
        // increment the scrolling position, adjust the position 
        // of the scroll box, and update the window. 
 
        if (xInc = max (-xPos, min (xInc, xMax - xPos))) 
        { 
            xPos += xInc; 
            ScrollWindowEx (hwnd, -xChar * xInc, 0, 
                (CONST RECT *) NULL, (CONST RECT *) NULL, 
                (HRGN) NULL, (LPRECT) NULL, SW_INVALIDATE); 
            si.cbSize = sizeof(si); 
            si.fMask  = SIF_POS; 
            si.nPos   = xPos; 
            SetScrollInfo(hwnd, SB_HORZ, &si, TRUE); 
            UpdateWindow (hwnd); 
        } 
 
        return 0; 
 
    case WM_VSCROLL: 
        switch(LOWORD (wParam)) 
        { 
            // User clicked the shaft above the scroll box. 
 
            case SB_PAGEUP: 
                 yInc = min(-1, -yClient / yChar); 
                 break; 
 
            // User clicked the shaft below the scroll box. 
 
            case SB_PAGEDOWN: 
                 yInc = max(1, yClient / yChar); 
                 break; 
 
            // User clicked the top arrow. 
 
            case SB_LINEUP: 
                 yInc = -1; 
                 break; 
 
            // User clicked the bottom arrow. 
 
            case SB_LINEDOWN: 
                 yInc = 1; 
                 break; 
 
            // User dragged the scroll box. 
 
            case SB_THUMBTRACK: 
                 yInc = HIWORD(wParam) - yPos; 
                 break; 
 
            default: 
                 yInc = 0; 
 
        } 
 
        // If applying the vertical scrolling increment does not 
        // take the scrolling position out of the scrolling range, 
        // increment the scrolling position, adjust the position 
        // of the scroll box, and update the window. UpdateWindow 
        // sends the WM_PAINT message. 
 
        if (yInc = max(-yPos, min(yInc, yMax - yPos))) 
        { 
            yPos += yInc; 
            ScrollWindow(hwnd, 0, -yChar * yInc, 
                (CONST RECT *) NULL, (CONST RECT *) NULL, 
                (HRGN) NULL, (LPRECT) NULL, SW_INVALIDATE); 
            si.cbSize = sizeof(si); 
            si.fMask  = SIF_POS; 
            si.nPos   = YPos; 
            SetScrollInfo(hwnd, SB_VERT, &si, TRUE); 
            UpdateWindow (hwnd); 
        } 
 
        return 0;