Click here to Skip to main content
15,798,826 members
Articles / Programming Languages / C++


Rate me:
Please Sign up or sign in to vote.
4.60/5 (4 votes)
30 Jan 2021CPOL7 min read 4.2K   159   2   3
ScrollCall is a basic GUI demo-like utility that demonstrates how controls and images are sized and scrolled.
ScrollCall is a demo program that takes a sample of Windows standard controls, along with a standard GDI image, and arranges them on a Device Context (or DC), in a window. Depending on the dimensions of the image, and the size of the containing window, horizontal and/or system scrollbars become visible, to enable scrolling for the image and controls. Thus ScrollCall is as at least as much focused on sizing as it is scrolling, and both offer unique challenges for the programmer, as will be seen in the following sections.

Image 1


ScrollCall features:

  • System scroll bars
  • Optional groupbox
  • Button to open images on the Device Context (DC)
  • Radio options for choice of window scroll function
  • Checkbox to stretch rather than scroll the image
  • Label Paint Mult with UpDown and Buddy to increase the wait times of WM_SIZE during sizing, thus reduced WM_PAINT processing
  • Right click for system snapshot of view in default or monitor attached to desktop
  • Double-click to print the visible part of the (mostly empty) client window to the DC, and back to the client window (experimental)
  • ScrollCall temporarily turns on SPI_SETDRAGFULLWINDOWS for the testing of the visual effects of dragging, if ever it was toggled off
  • Compatibility with AeroSnap sizing


The objective of a Graphical User Interface (GUI) based app is to promote a clear, uncluttered, and sizable form for ease of use.

ScrollCall considers the scenario of sizing, scaling and scrolling controls adjacent to an image background which can also be scrolled with the same scrollbar. A handy feature if any controls containing text are too small to read.

ScrollCall is intended to display the capabilities of the scrolling and sizing functions under a variety of conditions, but it has only been tested on (by today's standards at least), slow rigs.

In the next section, the purpose and utility of the main functions in ScrollCall will have the focus, leaving the finer details to the reader.

Using the Code

Although ScrollCall is tagged as C++, every attempt has been made to make it as "C-friendly" as possible, so that a future conversion to C may be possible. For best results, build ScrollCall as a debug in Visual Studio. Alternatively, a release executable is included in the attachment, or made available at Github.


In ScrollCall, as per requirement of ScrollWindow(Ex), we are obliged to use the WM_PAINT message to paint the output of the scrolling function. With form sizing, we choose to perform painting for the loaded images and system screenshot with the function AdjustImage().

BOOL AdjustImage(HWND hWnd, HBITMAP hBitmap, HBITMAP &hBitmapScroll, 
HGDIOBJ &hdefBitmap, HGDIOBJ &hdefBitmapScroll, BITMAP bmp, HDC& hdcMem, 
HDC& hdcMemIn, HDC& hdcMemScroll, HDC hdcScreen, HDC hdcScreenCompat, 
HDC hdcWinCl, UINT& bmpWidth, UINT& bmpHeight, int curFmWd, int curFmHt, 
int resizePic, int minMaxRestore, BOOL newPic)

This function deals with the handling of the system screenshot as well as any images loaded from the disk, so the parameter list is a bit "overloaded". Most notable are the memory DCs: hdcMem, hdcMemIn, hdcMemScroll, the handle to the system screenshot object, hdcScreen, our compatible version of it, hdcScreenCompat, and the window DC hdcWinCl for all image output to our client window. These constitute a fundamental part of our image sandbox.

AdjustImage() is divided into two sections each for both image and screenshot processing. The first section focuses more on manipulation of the data on the memory DCs, the second section is for faster Blting during sizing moves. If Stretch is toggled on, the image is painted to the limits of the window rectangle, so additional bitmap analysis is required to establish stretch cutoff co-ordinates to retain a reasonable image quality.

The painting for the printed window is performed by PaintScrolledorPrinted() (gotten by double-click) called from a timer. The timer is used to avoid possible complications with window (in)validation by scrollbar changes after sizing.

PaintScrolledorPrinted(PAINTSTRUCT *ps, HDC hdcWinCl, HDC hdcMem, 
int xNewSize, int yNewSize, UINT bmpWidth, UINT bmpHeight, BOOL printCl, BOOL noFill)

The printed image is not updated during sizing as can happen with the system screenshot.

Here's a typical implementation of BitBlt() in ScrollCall, where the action goes from right to left. The data from the rectangle in hdcMem is blt'd into hdcWinCl, the DC for our output client window, thus the visible effect is immediate.

BitBlt(hdcWinCl, wd - xCurrentScroll, -yCurrentScroll, bmpWidth + newPicWd, 
       bmpHeight, hdcMem, newPicWd, 0, SRCCOPY);


What scrolling function to choose? ScrollDC() was the function of choice in scrolling the printed window (got by double-click). This is partly because the generation of a complete printed window of a client window is performed in a different way to default image handling.

ScrollDC() works in a different way to the other functions. It will not populate PAINTSTRUCT's rcPaint with co-ordinates for convenient painting. ScrollDC() will also not scroll owned/child/parent/sibling windows which may be visible in the DC, so if there are controls not clipped out by the DC, the scrolling image will be visible under them. ScrollCall can also use the groupbox around the controls to hide the scrolling around them.

PaintScrolledorPrinted(PAINTSTRUCT *ps, HDC hdcWinCl, HDC hdcMem, int xNewSize, 
int yNewSize, UINT bmpWidth, UINT bmpHeight, BOOL printCl, BOOL noFill)

Thus PaintScrolledorPrinted(), mentioned in the last section, effectively complements AdjustImage() for all the painting in ScrollCall. The printed window scrolled by ScrollDC() never gets any data from *ps, but the window scrolling functions ScrollWindow(Ex()) do.

In comparing the two functions below, ScrollDC() does not scroll the rectangle containing the controls, whereas the magic required for ScrollWindowEx() is promulgated by the SW_SCROLLCHILDREN and SW_INVALIDATE flags.

ScrollDC(hdcWinCl, -xDelta, 0, (CONST RECT*) &rectControls, (CONST RECT*) &rectControls, 
(HRGN)NULL, (RECT*) &rectControls)
ScrollWindowEx(hWnd, -xDelta, 0, (CONST RECT*) NULL, (CONST RECT*) NULL, (HRGN)NULL, 

To test & compare usage with loaded images or system screenshot, we can choose either ScrollWindow() or ScrollWindowEx() within the program's GUI interface. Albeit little noticeable difference in the performance of the two, even on lightweight systems or power saver plans, the function recommended for general use today is ScrollWindowEx(). In most situations, ScrollWindow() operates the same way as ScrollWindowEx(), but it requires Invalidaterect() on the scrolled rectangle for WM_PAINT processing.

The data required for scrolling is managed by ScrollInfo(), which processes the various scrolling parameters determined from scrollXorY.

ScrollInfo(HWND hWnd, int scrollXorY, int scrollType, int scrollDrag, 
int xNewSize, int yNewSize, int bmpWidth, int bmpHeight, BOOL newPic)

Of most interest is when scrollXorY is zero, which is when a scrollbar is created, changed in some way, or destroyed. The scrollbar attributes are stored in the SCROLLINFO structure, where notably, the nPos values correspond with the well used global variables xCurrentScroll and yCurrentScroll. For example, the vertical height (nMax) of the system screenshot determines the range of values yCurrentScroll assumes.


For convenience, the default window size after load is based on a multiple of control width (wd) and height (ht), which are set as wd and ht for the Open Image button, half wd and full ht for the UpDown & Buddy, and wd and half ht for the rest of the controls. The function SizeControls() is able to modify the size of the controls, as well as their vertical separation.

SizeControls(int bmpHeight, HWND hWnd, int &yOldScroll, int resizeType, 
             int curFmWd, int curFmHt, BOOL newPic)

Variables like yOldScroll are required when it is desired for a replacement image to be positioned at the last scroll point rather than the zero default. As mouse speed is not tracked during sizing, ScrollCall generates its own timed sizing with the variables newCtrlSizeTriggerWd and newCtrlSizeTriggerHt. Because the Open Image button is always set at the top of the client window, the vertical sizing and separation of the other controls require the extra management of a function like delegateSizeControl().

delegateSizeControl(RECT rectOpt, HWND hWndOpt, int oldOptTop, 
int resizeType, int oldResizeType, int defOptTop, int yScrollBefNew, 
int newCtrlSizeTriggerHt, int newWd, int newHt, int minHt, BOOL newPic, HWND buddyHWnd)

ScrollCall uses the scaling factors scaleX and scaleY modified by GetDims() to meet the various conditions passed from SizeControls().

GetDims(HWND hWnd, int resizeType, int oldResizeType)

Of interest is during sizing, when firstSizeAfterSTART_SIZE_MOVE is required to set the scale based on the window's previous state. Further, for each image load, the precise dimensions of the client window with any combination of scrollbars flagged by scrollStat is calculated in:

InitWindowDims(HWND hWnd, int scrollStat, int& xNewSize, int& yNewSize, 
               UINT bmpWidth, UINT bmpHeight)

Finally, here's a typical implementation of a sizing function used in ScrollCall:

SetWindowPos(hWndButton, NULL, -xCurrentScroll, 
-yCurrentScroll, newWd, newHt, SWP_NOSENDCHANGING);

The flag SWP_NOSENDCHANGING is added to avoid an extra WM_SIZE message sent after this statement is executed.


If running ScrollCall through a debugger, the debug log shows extra info when non zero values of Paint Mult are set. Can be of use when considering the need for less graphic output on high end systems. The format for this output is set out in ReportErr().

ReportErr(const wchar_t* szTypes, ...)

For zero values of Paint Mult, we know there is just about one WM_PAINT processed for every WM_SIZE. For non zero values of Paint Mult, the debug program log outputs the frequency of each event, noting that as Paint Mult increases, the WM_SIZE hits decrease.

As the functionality of ScrollCall applies to within the dimensions of the current monitor, any painting from moving/sizing outside the screen cannot be relied upon. Consequently, we have:

IsAllFormInWindow(HWND hWnd, BOOL toolTipOn, BOOL isMaximized)

If a "monitor breach" has occurred, IsAllFormInWindow() calls CreateToolTipForRect() for the tooltip notification.

When the setting in Control Panel: System Properties > Advanced > Performance > Settings is turned off, SetDragFullWindow() turns it on temporarily to show the paint while sizing.

SetDragFullWindow(BOOL dragFullWindow, BOOL restoreDef)

The variable defDragFullWindow holds the setting until it is required to be restored in the function outlined next:

Last, but not least, is the return of ScrollCall memory allocations to the shared memory pool, by releasing, deleting, freeing, or just zeroing them. Kleenup() is also required before each loading or reloading of an image.

Kleenup(HWND hWnd, HBITMAP& hBitmap, HBITMAP& hBitmapScroll, HGDIOBJ hdefBitmap, 
HGDIOBJ hdefBitmapScroll, HBITMAP& hbmpCompat, GpBitmap*& pgpbm, HDC& hdcMem, 
HDC& hdcMemIn, HDC& hdcMemScroll, HDC& hdcWinCl, int typeOfDC, BOOL noExit)

Points of Interest

For further reading, this article on painting and this article on scrolling complement the above subject matter rather well.

For more information and ideas for expansion on the project, please refer the Github wiki, as well as ScrollCall's known issues, logged there.

Scroll up! Scroll up! for the last bit.

We all know something is unscaled because no-one has ever attempt to scale it, yet something that is unscrolled? We are all sure to have scrolled it!


  • 30th January, 2021: Version 1.0


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Written By
Australia Australia
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

GeneralMy vote of 4 Pin
Doom For Ever6-Feb-21 11:04
professionalDoom For Ever6-Feb-21 11:04 
Very nice job in a pure C style, this remembers me Charles PETZOLD, a writer of books about Windows (old fashion) Smile | :)
AnswerHelp please? Pin
simmuy235-Feb-21 8:18
simmuy235-Feb-21 8:18 
GeneralMy vote of 5 Pin
Michael Haephrati30-Jan-21 8:53
mvaMichael Haephrati30-Jan-21 8:53 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.